Android消息机制使用注意事项,防止泄漏
在Android的线程通信当中,使用频率最多的就是Android的消息处理机制(Handler.send()、View.post()、Asynctask.excute()等等都使用到了消息处理机制)。Android中UI线程默认实现了该机制,其它工作线程要想跟UI线程一样拥有该机制,就必须人为去实现该机制,该机制的实现也相当简单暂且忽略。对于Android里的消息处理,涉及到Handler,Looper,Message,Message Queue等概念。
- Message:消息,其中包含了what(消息ID),obj(消息处理对象,这是引起泄漏的主要原因,下面会谈到)以及其它处理的数据(arg1,arg2,messenger),由MessageQueue统一列队,终由Handler处理。
- Handler:线程消息管理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。
- MessageQueue:线程的消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。
- Looper:一个线程只可以产生一个Looper对象,用来管理MessageQueue,它就像一个消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。
为了便于理解Message泄漏,首先看一段Message的源代码:
// sometimes we store linked lists of these things /*package*/ Message next; private static final Object sPoolSync = new Object(); private static Message sPool; private static int sPoolSize = 0; private static final int MAX_POOL_SIZE = 50; /** * Return a new Message instance from the global pool. Allows us to * avoid allocating new objects in many cases. */ public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; sPoolSize--; return m; } } return new Message(); }
从以上代码可以看到Message类有一个全局的消息池,池的大小为50,用于存放Message对象。Message引起泄漏的问题就出在这个全局的消息池。我们知道Java的回收是由GC来进行的,而当我们强引用着一个对象时,GC是不会将这个对象回收的。正好这个全局的消息池,里面存储的Message必然是一个强引用。造成Message泄漏的一个最大的根源是Message的obj字段,这个字段是Object类型的,因此obj的byte数是可以很大的。
有些人可能会说,那我对obj这个参数做弱引用让GC能够回收好不就得了。这个方法虽然能解决GC的回收,但是有一个致命的问题,就是弱应用是极不安全的,GC想要什么时候回收弱应用对象就什么时候回收。所以我们应该换一个解决方方法,处理完一个Message就从全局当中移除一个。对于这个实现,Android的Handler有三个暴露的方法可供使用,removeMessages(int what)、removeMessages(int what, Object object)和removeCallbacksAndMessages(Object token)。具体使用可见如下代码:
@Override public void handleMessage(android.os.Message msg) { if (msg == null) { return; } removeXXXXX(); } }
removeMessages(int what)和removeMessages(int what, Object object)会调用MessageQueue的removeMessages(Handler h, int what, Object object),该方法源码如下:
void removeMessages(Handler h, int what, Object object) { if (h == null) { return; } synchronized (this) { Message p = mMessages; // Remove all messages at front. while (p != null && p.target == h && p.what == what && (object == null || p.obj == object)) { Message n = p.next; mMessages = n; p.recycle(); p = n; } // Remove all messages after front. while (p != null) { Message n = p.next; if (n != null) { if (n.target == h && n.what == what && (object == null || n.obj == object)) { Message nn = n.next; n.recycle(); p.next = nn; continue; } } p = n; } } }
removeCallbacksAndMessages(Object token)会调用MessageQueue的removeCallbacksAndMessages(Handler h, Object object)方法,该方法源码如下:
void removeCallbacksAndMessages(Handler h, Object object) { if (h == null) { return; } synchronized (this) { Message p = mMessages; // Remove all messages at front. while (p != null && p.target == h && (object == null || p.obj == object)) { Message n = p.next; mMessages = n; p.recycle(); p = n; } // Remove all messages after front. while (p != null) { Message n = p.next; if (n != null) { if (n.target == h && (object == null || n.obj == object)) { Message nn = n.next; n.recycle(); p.next = nn; continue; } } p = n; } } }