在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;
}
}
}

Android消息机制使用注意事项,防止泄漏的更多相关文章

  1. Android消息机制

    每一个Android应用在启动的时候都会创建一个线程,这个线程被称为主线程或者UI线程,Android应用的所有操作默认都会运行在这个线程中. 但是当我们想要进行数据请求,图片下载,或者其他耗时操作时 ...

  2. Android开发之漫漫长途 ⅥI——Android消息机制(Looper Handler MessageQueue Message)

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  3. Android开发之漫漫长途 Ⅶ——Android消息机制(Looper Handler MessageQueue Message)

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  4. Android 进阶14:源码解读 Android 消息机制( Message MessageQueue Handler Looper)

    不要心急,一点一点的进步才是最靠谱的. 读完本文你将了解: 前言 Message 如何获取一个消息 Messageobtain 消息的回收利用 MessageQueue MessageQueue 的属 ...

  5. 每日一问:Android 消息机制,我有必要再讲一次!

    坚持原创日更,短平快的 Android 进阶系列,敬请直接在微信公众号搜索:nanchen,直接关注并设为星标,精彩不容错过. 我 17 年的 面试系列,曾写过一篇名为:Android 面试(五):探 ...

  6. 史上最详细的Android消息机制源码解析

    本人只是Android菜鸡一个,写技术文章只是为了总结自己最近学习到的知识,从来不敢为人师,如果里面有不正确的地方请大家尽情指出,谢谢! 606页Android最新面试题含答案,有兴趣可以点击获取. ...

  7. Android消息机制:Looper,MessageQueue,Message与handler

    Android消息机制好多人都讲过,但是自己去翻源码的时候才能明白. 今天试着讲一下,因为目标是讲清楚整体逻辑,所以不追究细节. Message是消息机制的核心,所以从Message讲起. 1.Mes ...

  8. Android消息机制不完全解析(上)

        Handler和Message是Android开发者常用的两个API,我一直对于它的内部实现比较好奇,所以用空闲的时间,阅读了一下他们的源码.    相关的Java Class: androi ...

  9. Android消息机制不完全解析(下)

    接着上一篇文章Android消息机制不完全解析(上),接着看C++部分的实现. 首先,看看在/frameworks/base/core/jni/android_os_MessageQueue.cpp文 ...

随机推荐

  1. 11-6-es5选项卡

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. Leetcode961. N-Repeated Element in Size 2N Array重复N次的元素

    在大小为 2N 的数组 A 中有 N+1 个不同的元素,其中有一个元素重复了 N 次. 返回重复了 N 次的那个元素. 示例 1: 输入:[1,2,3,3] 输出:3 示例 2: 输入:[2,1,2, ...

  3. Leetcode300. Longest Increasing Subsequence最长上升子序列

    给定一个无序的整数数组,找到其中最长上升子序列的长度. 示例: 输入: [10,9,2,5,3,7,101,18] 输出: 4 解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4. 说 ...

  4. leetcode 75 Sorted Colors

    两种解法 1)记录0和1的个数 然后按照记录的个数将0和1重新放入原数组,剩下的补2 2)双指针left,right left表示0~left-1都为0,即i之前都为0 right表示right+1~ ...

  5. Apache SOLR and Carrot2集成

    1.环境 下载软件 名称 地址 solr-integration-strategies-gh-pages.zip https://github.com/carrot2/solr-integration ...

  6. Redis多API开发

    目录 Redis API支持 redis-py安装方式 Python 连接redis 直接连接 使用连接池连接 Windows 连接redis数据库 一.下载Redis Desktop Manager ...

  7. Django WSGI响应过程之WSGIHandler

    class WSGIHandler(base.BaseHandler): request_class = WSGIRequest def __init__(self, *args, **kwargs) ...

  8. Spring Boot实战系列(7)集成Consul配置中心

    本篇主要介绍了 Spring Boot 如何与 Consul 进行集成,Consul 只是服务注册的一种实现,还有其它的例如 Zookeeper.Etcd 等,服务注册发现在微服务架构中扮演这一个重要 ...

  9. 洛谷P2827 蚯蚓

    传送门 pts85/90(90应该是个意外,第一次交是90之后都是85了): 优先队列模拟题意 #include<iostream> #include<cstdio> #inc ...

  10. webServices学习三(概念详解)

    WebService通过HTTP协议完成远程调用: (深入分析) WebService只采用HTTP POST方式传输数据,不使用GET方式; -- 握手,WSDL-get, 普通http post的 ...