关于Handler,你应该知道的
Handler、Looper、Thread、Message、MessageQueue的关系
每一个线程对象,都有且只有一个Looper对象与之关联。线程的Looper对象创建后,会自动创建一个MessageQueue作为该线程的消息队列。也就是说一个Thread对象对应一个Looper对象,对应一个MessageQueue。
创建Handler时,如果没有指定与之关联的Looper对象,那就默认和创建Handler的线程的Looper对象相关联。这里需要提到两点:
- 通常情况下,一个线程不会自动创建它关联的Looper对象。这意味着我们在子线程中创建Handler对象时,要先确定该线程的Looper对象是否已创建。如果Looper对象没有创建,则需调用Looper.prepare()来为线程创建对应的Looper对象。或者直接关联主线程的Looper对象。
- 在Android系统中,UI主线程ActivityThread默认会自动创建关联的Looper对象。
因为可以在一个线程里创建多个Handler,也就是说Looper和Handler是一对多的关系。一个Handler对象只能关联一个Looper对象。
MessageQueue用来存放Handler发送的Message对象。MessageQueue由Looper管理。从MessagaQueue取出Message对象,分发给对应的Handler对象去处理。Looper通过Message对象的target属性,找到处理该消息的Handler对象。
Message 和 Runnable
当我们需要Handler发送一个Message时,不建议直接通过new Message()创建一个新的Message对象。更推荐使用Message.obtain()来获取Message对象。因为Message类中定义了一个Message对象池,它是一个单向链表。
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
也就是说,当对象池不为空时,可重复利用闲置的Message对象。如此,可以避免创建过多的对象而产生GC问题。
Handler不仅可以发送Message对象,还可以发送Runnable到消息队列。
一般发送Message对象是用于在线程间传递数据。而发送Runnable则是用于执行定时任务。从Handler类的源码可以看出,发送Runnable,还是需要将其包装成Message对象。
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;
}
Callback 和 handleMessage
细心的朋友会发现,Handler有这样一个构造函数,它需要一个Callback类型的参数,这个Callback是个接口,它也定义了一个handleMessage方法。
public Handler(Callback callback) {
this(callback, false);
}
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public boolean handleMessage(Message msg);
}
我们知道,Handler类本身也有一个handleMessage方法。那么他们之间的区别是什么呢
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
区别就在于,调用dispatchMessage方法处理消息时,是否需要执行Callback.handleMessage()方法。如果执行Callback.handleMessage(),则Handler的handleMessage()方法不会执行。
Handler引发的内存泄漏
我们刚接触Handler时,一般的用法是类似这样的:
public class MainActivity extends Activity {
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Thread mThread = new Thread(){
@Override
public void run() {
super.run();
try {
sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendMessage(new Message());
}
};
mThread.start();
}
}
如此,在子线程中执行耗时的操作,需要较长的时间才能执行完。
在java中,非静态内部类会默认持有外部类的强引用。这样的结果就是,在子线程执行耗时操作时,如果外部的Activity出现页面退出或关闭,那么原本应该被回收的Activity对象,会由于Handler对象还持有外部Activity对象的强引用,而导致Activity对象不能被回收,从而导致内存泄露。
这个问题的解决方案是:
1.将Handler类声明为静态类。如果处理消息需要用到外部Activity的引用,可以设为弱引用。
private static class StaticHandler extends Handler{
WeakReference<Activity> activity;
public StaticHandler(Activity activity){
this.activity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (activity.get()!=null){
}
}
}