1 概述
前面几篇介绍了Handler、Looper、MessageQueue的源码实现原理,理解了原理,下一步我们要知道的就是怎么用好这些内容.
2 获取Message对象
首先,不建议使用new Message
方法,而是使用Message.obtain()
方法,因为obtain()
方法内部有一套消息池机制,首先从消息池获取可用消息,没有的时候才新建消息,这样就避免创建多余的消息对象造成内存浪费。
获取Message的方法:
1 Messgae.obtain() 优先从消息池中获取消息,没有空闲消息再创建。
2 Message.obtain(Message msg) 获取一个msg消息的拷贝
3 handler.obtain() 获取消息的同时指定这个消息的target为调用Handler
3 主线程中使用
1、主线程中默认就已经初始化一个Looper,故可以直接new Handler()
默认的空构造方法生产一个主线程的Handler,然后通过handler的sendMessage或post方法,完成消息的发送,再通过复写handler的 handlerMessage方法处理消息的回调。
2、有一点需要注意的是,由于sendMessage的方法处理,或者是post的Runnable,最终都是在主线程,也就是UI线程中执行的,故不要放耗时操作,避免ANR
3 子线程中获取主线程的Looper和MessageQueue
Android的Looper默认提供了一个静态方法Looper.getMainLooper(),用于获取主线程的Looper对象。其实现原理,前面已经提过,主线程的looper会默认存储在全局变量sMainLooper中,通过该方法获取到。拿到looper对象后,就可以通过looper.getQueue()获取该Looper的MessageQueue,通过new Handler(looper),获取主线程的handler了。此时,拿到主线程的handler,就可以发处理UI的消息给主线程执行了。
4子线程中使用Looper
首先需要初始化一个looper
class MyLooperThread extends Thread{
public void Handler handler;
public void run(){
Looper.prepare();
handler = new Handler(){
public void handlerMessage(Message msg){
//...处理消息回调,子类实现具体业务
}
};
Looper.loop();//这里进入循环
}
}
初始化完成后,需要再次获取此looper,则通过Looper.myLooper()
,获取当前线程的looper。可以通过Looper.myQueue()方法用于获取当前线程的Looper的MessageQueue。此时也可以通过new handler()
创建一个handler(默认使用Looper.myLooper()取到的looper,也就是子线程的looper),此handler是子线程的handler,而不是UI线程的Handler,不能用此handler来更新UI
5 runOnUiThread
这个方法位于activity类中,实现也是通过主线程的handler,post一个Runnable给主线程的handler执行。所以同样,此方法传递的Runnable中也不要有耗时操作。
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
6 消息的取消、查询和发送
sendMessage发送的消息通过removeMessage(int what)来取消
post发送的消息,通过removeCallback(Runnable action)来取消
hasMessages(int what) 查询当前是否含有what的消息
hasCallbacks(Runnable r) 查询当前是否有r这个Runnable
postAtFrontOfQueue 把消息/Runnable插入到队列头部
7Handler可能引起的内存泄露
Handler作为内部类的时候,由于内部类会持有外部类对象的一个引用,故此Handler可能导致外部类的实例对象无法被回收。此时,外部若是一个activity,则其onDestory方法一直不会被调用。
解决方式一:设置该Handler为static,静态方法或者属性为类的方法或属性,而不是对象的方法或者属性,故不会持有外部类的对象的引用,也就不会导致外部对象无法被回收。
解决方式二:使用弱引用实现,定义一个工具类,封装其若引用。
public class HandlerUtil {
private static int mId = 0x1000000;
public interface MessageListener {
public void handleMessage(Message msg);
}
public static final int generateId() {
return ++ mId;
}
public static class StaticHandler extends Handler {
WeakReference<MessageListener> listener;
public StaticHandler(MessageListener listener) {
super();
this.listener = new WeakReference<MessageListener>(listener);
}
public StaticHandler(Looper looper, MessageListener listener){
super(looper);
this.listener = new WeakReference<MessageListener>(listener);
}
public StaticHandler() {
super();
}
@Override
public void handleMessage(Message msg) {
MessageListener listener = this.listener.get();
if (listener != null) {
listener.handleMessage(msg);
}
}
}
}
说明:
-
- 通过generateId()方法生成一个唯一的ID,App全局唯一,这样可以防止可能发生的消息ID重复而导致的各种问题。
-
- 定义一个StaticHandler静态Handler,继承原Handler接口, 用来消除Handle可能导致的泄漏。
-
- StaticHandler中含有一个弱引用
WeakReference<MessageListener> listener
,在构造的时候传入此弱引用的值。此listener必须由Activity实现该接口(推荐)或者是宿主Activity的类成员,因为弱引用不会增加引用计数,若是使用匿名变量则会导致listener过早释放
- StaticHandler中含有一个弱引用
-
- 最终调用到Handler的handlerMessage处理消息,在这个方法内部,通过this.listener.get() 获取listener的强引用,获取成功的时候再回调其handlerMessage方法,也就最终实现了消息的处理。
8 HandlerThread
android提供了一个HandlerThread方法,简单封装了子线程实现handler looper机制,方便上层使用。此HandlerThread的使用方法和主线程的使用方法类似,只是不能从通过它更新UI。