在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#Image和Icon的相互转化

    Image img = Image.FromHbitmap(icon.ToBitmap().GetHbitmap()); Graphics g = Graphics.FromImage(img); g ...

  2. 从0开始学习ssh之岗位管理

    岗位应该有如下属性,id.名称.说明.新建role.java.添加id,name,description并增加三个的get set方法. 之后建立Role.hbm.xml 并加入到applicatio ...

  3. Unity 在2D中实现LookAt

    由于Transform.LookAt是使Z轴看向目标,而2D基本是由X和Y轴构成的.所以在2D的游戏中,不太好使用. 所以用代码实现一个2D的LookAt函数. 例子: 我们使怪物的眼睛一直看着云. ...

  4. python基础---内置函数 和 匿名函数 知识点自查填空题

    1.file ---默认是输出到(),如果设置为(),输出到() 2.sep---打印(),默认为() 3.end---每一次打印的结尾,默认为() 4.flush---立即把内容输出到(),不做() ...

  5. jeecms 链接标签

    .引入页面 [#include "../include/header-site.html"/]12.导航栏只有前两个带链接 [#if c_index<2] href=&quo ...

  6. CentOS7.4 安装JDK 步骤

    1.先在官网下载jdk1.8的压缩文件 2.用putty将压缩文件拷到home目录下 3.新建一个/home/jdk1.8目录 :  mkdir  /home/jdk1.8 4.将压缩文件解压到hom ...

  7. 转:Linux--进程间通信(信号量,共享内存)

    源地址:http://www.cnblogs.com/forstudy/archive/2012/03/26/2413724.html Linux--进程间通信(信号量,共享内存)(转)   一. 信 ...

  8. Python爬虫笔记【一】模拟用户访问之Tesseract-ocr验证码训练(5)

    验证码处理之后就需要对处理的验证码进行识别训练,这里用Tesseract-ocr工具进行识别,用jTessBoxeditor进行训练生成模板. 一,对图片进行处理 利用上一篇代码对图片进行降噪处理,得 ...

  9. 【转载】Linux Examination

    原博地址:https://blog.csdn.net/weixin_42568655/article/details/94603660 (来自我的同学QiaoGuangtong大佬) Fundamen ...

  10. Vuejs实战项目步骤一

    1.使用vue初始化项目 vue create msm-demo #创建项目 npm run serve #部署 2.更改public文件夹下面的index文件,只留下 <div id=&quo ...