基本要素
- Message:消息的表示
- MessageQueue:消息队列
- Looper:消息循环,用于从消息队列中取出消息
- Handler:消息处理
Handler的使用
- 初始化
为了使用Handler,必须初始化。
- 在主线程中,已经预先初始化了,所以可以直接使用Handler.
- 而在自己创建的线程中,需要首先调用Looper.prepare();方法用来初始化MessageQueue和实例化Looper并设置Looper作为ThreadLocal的值,然后调用Looper.loop();方法进行消息的循环和处理。
- 或者在构造时使用已经初始化的Looper,这样的话,消息的处理依然是在Looper初始化时所在的线程。
-
扩展Handler或者实现Handler.Callback接口作为Handler的构造参数,并实现handleMessage方法。实例化Handler子类时,应该在prepare和loop之间进行。
-
获得Handler对象实例,调用方法发送消息到消息队列。消息可以是Runnable或Message类型,分别是使用post和send方法。
HandlerThread的使用
HandlerThread thread = new HandlerThread("thread name" );
thread.start (); // 要把线程启动
Handler handler = new Handler(thread.getLooper ()) {
// 实现handleMessage方法
}
因为HandlerThread的run方法中,已经调用Looper的prepare和loop方法,这个线程已经在等待消息了。
Handler的构造函数中,使用了上面线程的Looper,因此,发送给Handler的消息会放到上面线程的Queue中,且在上面线程中执行。
post和sendMessage
post发送的Runnable最终被封装为Message,其中Runnable作为Message的callback域的值。
然后当Looper的消息循环收到消息时,根据Message的target域中的值,会调用对应Handler的dispatchMessage方法。
从方法中可以看到,如果Message的callback不为空(也就是使用post发送),那么直接调用Runnable的run方法,也就是说,在Looper所在的线程中同步处理。
否则,则在handleMessage方法中处理。默认的handleMessage方法为空。
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r)
{
Message m = Message.obtain();
m.callback = r;
return m;
}
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
}
else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
Handler可能引起的内存泄漏和解决方案
首先,Looper的prepare方法会设置Looper到ThreadLocal中,而ThreadLocal变量作为Looper的static变量,又因为Message的target字段中包含Handler的引用,而Message被放入消息队列,所以导致Handler的生命周期比Activity长。
如果Handler在Activity作为内部类实现,那么就会保存一个Activity的引用。或者消息队列中包含尚未处理的Message,当Activity销毁后,导致Activity不能被垃圾回收。
解决办法:
- Handler作为静态类或外部类实现
- Activity作为参数传递给Handler的构造器
- Handler内部使用软引用或弱引用来保存Activity。
- 在handleMessage或run方法中使用Activity时,应该判断它是否为空。
- 另外,在Handler的post参数Runnable,也不应该使用内部类。
另外,当Activity销毁时,还在消息队列中排队的任务应该被取消掉,否则Activity还是不能被垃圾回收。可以通过调用Handler中的remove..方法或者mHandler.removeCallbacksAndMessages(null); 删除所有的Runnable和Message。
参考
Android消息处理机制(Handler、Looper、MessageQueue与Message)
Android App 内存泄露之Handler
网友评论