在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. C#用API可以改程序名字

    [DllImport("user32.dll", EntryPoint = "FindWindow")] public static extern int Fi ...

  2. hiveUDF的使用

    在此自己总结下UDF的用法 1.首先最简单的UDF(普通用java扩充函数的方式,大多数简便函数可以用这个函数来实现,返回单个字段),其加强版UDGF据说对map一类数据类型有更好兼容,实现上略复杂 ...

  3. Mount- Linux必学的60个命令

    1.作用 mount命令的作用是加载文件系统,它的用权限是超级用户或/etc/fstab中允许的使用者. 2.格式 mount -a [-fv] [-t vfstype] [-n] [-rw] [-F ...

  4. mybatis配合pagehelper分页助手查询

    Maven: 参考: springBoot2.x整合pagehelper5.1.2:https://blog.csdn.net/Carlson_Chis/article/details/8563748 ...

  5. 性能压测中的SLA,你知道吗?

    本文是<Performance Test Together>(简称PTT)系列专题分享的第6期,该专题将从性能压测的设计.实现.执行.监控.问题定位和分析.应用场景等多个纬度对性能压测的全 ...

  6. c++容器的操作方法总结

    一.map 1.创建 typedef map<int,string> descrbe_map_; descrbe_map_ devMap; 或者 map<string,string& ...

  7. LUOGU P2986 [USACO10MAR]伟大的奶牛聚集Great Cow Gat…

    传送门 解题思路 首先第一遍dfs预处理出每个点的子树的siz,然后可以处理出放在根节点的答案,然后递推可得其他答案,递推方程 sum[u]=sum[x]-(val[i]*siz[u])+(siz[1 ...

  8. linux学习(二)-----Linux 的目录结构、远程登录、vi和vim

    linux目录结构 基本介绍 linux 的文件系统是采用级层式的树状目录结构,在此结构中的最上层是根目录“/”,然后在此 目录下再创建其他的目录. 目录结构具体介绍 Linux 目录总结 1.lin ...

  9. eclipse中运行java程序

    1 package ttt; public class Testttt { public static void main() { Person p =new Person(); p.name=&qu ...

  10. 011-python列表,元组,字典的用法

    1.列表 1.1 列表截取 list[头下标:尾下标:步长],例如:L[1:10:2] >>> L [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] >>& ...