Handler 作用
Android 中的Handler 主要用于主线程与子线程之间的通信,也就是处理异步消息。
简单理解来说主线程无法处理耗时任务,不然就会阻塞线程,那么就会用子线程来异步处理耗时工作,而子线程无法直接更新UI界面,所以需要handler来传递消息给主线程,让主线程完成UI界面的更新工作。
概念:MessageQueue和Looper
MessageQueue用来保存子线程从Handler所发送未处理的消息,就是个消息队列,负责消息的入列出列。
Looper依次取出MessageQueue中的消息传递给主线程响应处理。
Handler原理是什么?
一、Handler封装了消息的发送 (主要包括消息发送给谁)
Looper
1、内部包含一个消息对列,也就是MessageQueue,所有的handler发送的消息都走向这个消息对列。
2、Loopler.looper方法,就是一个死循环,不断地从MessageQueue取消息,如果有消息就处理消息,没有消息就阻塞
二、MessageQueue,就是一个消息对列,可以添加消息,并处理消息
三、handler也很简单,内部会跟Looper进行关联,也就是说在handler的内部可以找到Looper,找到Looper也就找到了MessageQueue,在handler中发送消息,其实就是向MessageQueue队列中发送消息
总结:handler负责发送消息,Looper负责接收handler发送的消息,并直接把消息回传给handler自己。MessageQueue就是一个存储消息的容器
用法:Handler基本用法
android.os.Handler handler = new Handler(){
@Override
public void handleMessage(final Message msg) {
//这里接受并处理消息
}
};
//发送消息
handler.sendMessage(message);
handler.post(runnable);
实例化一个 Handler 重写 handleMessage 方法 ,然后在需要的时候调用它的 send 以及 post 等方法就可以了,非常简单易用,并且支持延时消息操作。
Handler 与 Looper 的关联
一般在实例化 Handler 的时候, Handler 会去检查当前线程的 Looper 是否存在,如果不存在则会报异常,在创建 Handler 之前一定需要先创建 Looper 。
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
Looper.prepare() :
也就是说 Handler 跟线程的关联是靠 Looper 来实现的。
Message 的对象
message.what=1;//what属性
message.arg1=i;//arg属性
message.obj="消息内容:";//obj属性
handler.sendMessage(message);
if (msg.what==1)//识别判断消息
{
//todo something
}
Message 的存储与管理
Handler 提供了一些列的方法让我们来发送消息,如 send()系列 post()系列 。
以 sendEmptyMessage(int) 方法为例:
//Handler
sendEmptyMessage(int)
-> sendEmptyMessageDelayed(int,int)
-> sendMessageAtTime(Message,long)
-> enqueueMessage(MessageQueue,Message,long)
-> queue.enqueueMessage(Message, long);
最终都会走到 Message.enqueueMessage(Message,long) 方法。
上面我们可以看到消息的管理者是 MessageQueue
MessageQueue用来保存子线程从Handler所发送未处理的消息,就是个消息队列,负责消息的入列出列。
Handler 引起的内存泄漏原因
Handler 允许我们发送延时消息,如果在延时期间用户关闭了 Activity,那么该 Activity 会泄漏。
这个泄漏是因为 Message 会持有 Handler,而又因为 Java 的特性,内部类会持有外部类,
使得 Activity 会被 Handler 持有,这样最终就导致 Activity 泄漏。
解决方案
将 Handler 定义成静态的内部类,在内部持有 Activity 的弱引用,并及时移除所有消息。
示例:
private static class SafeHandler extends Handler {
private WeakReference<HandlerActivity> ref;
public SafeHandler(HandlerActivity activity) {
this.ref = new WeakReference(activity);
}
@Override
public void handleMessage(final Message msg) {
HandlerActivity activity = ref.get();
if (activity != null) {
activity.handleMessage(msg);
}
}
}
并且再在 Activity.onDestroy() 前移除消息,加一层保障:
@Override
protected void onDestroy() {
safeHandler.removeCallbacksAndMessages(null);
super.onDestroy();
}
这样双重处理,就能避免内存泄漏。
注意:单纯的在 onDestroy 移除消息并不保险,因为特定情况下 onDestroy 并不一定执行。
常见Toast crash
当我们尝试在子线程里直接去弹 Toast 的时候,会发生crash :
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
本质上是因为 Toast 的实现依赖于 Handler,按子线程使用 Handler 的要求修改即可,同理的还有 Dialog。
正确示例代码如下:
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Toast.makeText(HandlerActivity.this, "正常Toast啦!", Toast.LENGTH_SHORT).show();
Looper.loop();
}
});
以上粗略总结也是日常常识以及在学习中参考大佬们的总结。不喜匆喷,欢迎指正。_
---by mars_chu 啥都不会的楚楚
网友评论