版权声明:本文出自汪磊的博客,转载请务必注明出处。

上一篇中共同探讨了ThreadLocal,这篇我们一起看下常提到的Message消息池到底是怎么回事,废话少说吧,进入正题。

对于稍有经验的开发人员来说我们在使用Handler发送异步消息获取Message的时候都会使用如下代码获取一个Message对象:

 Message msg = mHandler.obtainMessage();

而不是直接new一个:

 Message msg = new Message();

二者的主要区别就是上面的用到缓存池概念,如果池中有闲着的则拿来用,没有则new一个Message。后者则没有这个机制,直接new一个拿来用。

接下来我们分析一下这个缓存池是怎么实现的。

Message缓存池源码分析

Handler中obtainMessage()方法实质还是调用的Message中obtain()方法,这里就直接看Message中obtain()方法源码了:

 public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}

第3行首先判断sPool是否为null,如果为null则直接执行12行直接new一个Message返回,整个方法结束,sPool是什么鬼?定义如下:

 private static Message sPool;

看到了吧,就是一个Message对象,sPool其实就相当于一个头指针,指向缓存池中第一个缓存的Message,分析完所有就会自然明白了其作用。

继续向下分析。

sPool不为null则进入4-9行代码逻辑,sPool不为null说明缓存池中存在空闲的Message.

第4行记录sPool,并且第9行返回m作为整个方法的返回值,也就是返回缓存池中的空闲Message供外部使用,不需要额外内存开销。

第5行sPool指向下一个缓存对象。

第6行m.next置为null,到这里最重要的逻辑就完了,也许你还蒙蔽呢,这是什么啊,其实很简单Message的缓存池其实就是用了一个数据结构-单向链表。

接下来又要展示我强大的画图能力了,没有什么是一个图示不能解决的:

假设此时缓存池中有三个空闲message:message1,message2,message3。sPool一开始指向头部message1。

执行第4行代码相当于图中步骤①,没什么好解释的。

执行第5行代码相当于图中步骤②,sPool指向下一个缓存message,此处为message2.

执行第6行代码相当于图中步骤③,message1与message2断开连接。

怎么样这样解释该明白了,其实本身就很简单。

7,8行就是清除标记以及改变sPoolSize大小,sPoolSize用来记录缓存池中存在的元素个数,缓存池大小是有限制的,超过规定大小则不能再往里面添加。

obtain()总结

好了,到此主要逻辑就分析完了,obtain()主要逻辑就是先判断缓存池中是否存在空闲message,如果存在则返回头部message,并且指针指向下一个空闲message,然后头部的message与之后链表  断开连接。如果不存在空闲message则直接new一个直接返回。

上面的逻辑都是从缓存池中获取的操作,那什么时候向缓存池中存放呢?我们继续向下分析。

Message类中recycle()方法是用于回收用完的mesage,将此message会收到缓存池中,是这样的吗?我们看下源码就知道了:

 public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}

recycle方法中主要判断当前message是否正在使用中,如果正在使用则抛出异常,没被使用则调用recycleUnchecked()方法,接下来看下recycleUnchecked():

 void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null; synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}

4-14主要就是清除一些当前标记。

17行,MAX_POOL_SIZE就是规定的缓存池中最多缓存message的个数,如果当前已经存储的数量小于规定的最大缓存个数则继续向下执行。

18,19行就是重点了,又到展示我强大画图能力的时候了,一张图解决:

比如缓存池中链表中为message2,message3,sPool指向头部message2。

此时,message1被回收执行recycle()操作。最终执行到recycleUnchecked()的18,19行逻辑。

18行:相当于将图中message1的next指针指向sPool,此时sPool指向message2,也就是将message1与message2链接,也就是图中①操作。

19行:sPool重新定位到当前被回收的message,这里也就是message1。相当于图中②操作

recycle()总结

好了,到这里回收就讲完了,最主要就是18,19行逻辑,recycle()最主要就是将当前message放入缓存池链表头部。

到此,我想讲解的就完了,本篇核心就是数据结构中单项链表的实际应用,如果单向链表你很熟悉,我觉得这里应该很轻松的就理解了,即使不是很熟悉,用心思考一下也应该能理解,这里不难理解。

本篇就到此为止了,下一篇Android异步消息处理机制完全解析。

Android 异步消息处理机制前篇(二):深入理解Message消息池的更多相关文章

  1. Android 异步消息处理机制前篇(一):深入理解ThreadLocal

    版权声明:本文出自汪磊的博客,转载请务必注明出处. ThreadLocal简介 ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获 ...

  2. Android 异步消息处理机制终结篇 :深入理解 Looper、Handler、Message、MessageQueue四者关系

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.概述 我们知道更新UI操作我们需要在UI线程中操作,如果在子线程中更新UI会发生异常可能导致崩溃,但是在UI线程中进行耗时操作又会导致ANR,这 ...

  3. 篇二、理解Android Studio的视图和目录分析,这个是转载

    看不清的话可以可以将图片在新窗口中打开,以原图的大小显示.   原文链接:http://blog.csdn.net/siyehuazhilian/article/details/42123563   ...

  4. postgres高可用学习篇二:通过pgbouncer连接池工具来管理postgres连接

    安装pgbouncer yum install libevent -y yum install libevent-devel -y wget http://www.pgbouncer.org/down ...

  5. Android异步处理系列文章四篇之二 使用AsyncTask异步更新UI界面

    Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面Android异步处理二:使用AsyncTask异步更新UI界面Android异步处理三:Handler+Loope ...

  6. Android异步处理系列文章四篇之三

    Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面Android异步处理二:使用AsyncTask异步更新UI界面Android异步处理三:Handler+Loope ...

  7. Android异步处理系列文章四篇之四 AsyncTask的实现原理

    Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面Android异步处理二:使用AsyncTask异步更新UI界面Android异步处理三:Handler+Loope ...

  8. Android异步处理系列文章四篇之一使用Thread+Handler实现非UI线程更新UI界面

    目录: Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面Android异步处理二:使用AsyncTask异步更新UI界面Android异步处理三:Handler+L ...

  9. Android异步消息机制

    Android中的异步消息机制分为四个部分:Message.Handler.MessageQueue和Looper. 其中,Message是线程之间传递的消息,其what.arg1.arg2字段可以携 ...

随机推荐

  1. 关于NOIP2014“无线网络发射器选址”一题的衍生题目的思考及思维方向

    无线网络发射器选址 题目描述 随着智能手机的日益普及,人们对无线网的需求日益增大.某城市决定对城市内的公共场所覆盖无线网. 假设该城市的布局为由严格平行的129 条东西向街道和129 条南北向街道所形 ...

  2. 谈谈你对web标注和W3c的理解和认识

    web标准简单来说可以分为结构.表现和行为.其中结构主要是有HTML标签组成.表现即指css样式表,通过css可以是页面的结构标签更具美感.行为是指页面和用户具有一定的交互,同时页面结构或者表现发生变 ...

  3. C++函数返回值(02)

    对象作为返回值 编译器会将函数栈中的返回值数据拷贝到返回栈中 指针作为返回值 函数的返回值可以是存储某种类型数据的内存地址,称这种函数为指针函数.它们的一般定义形式如下:  类型标识符 *函数名(参数 ...

  4. (原创)(四)机器学习笔记之Scikit Learn的Logistic回归初探

    目录 5.3 使用LogisticRegressionCV进行正则化的 Logistic Regression 参数调优 一.Scikit Learn中有关logistics回归函数的介绍 1. 交叉 ...

  5. shell脚本学习(一):shell脚本开发的基本规范和习惯

    1.脚本第一行指定脚本解释器 #!/bin/bash 或 #!/bin/sh 2.脚本开头增加作者.脚本作用描述等信息 1 #!/bin/bash 2 #Author: iskylite 3 #Blo ...

  6. A+B problems

    这几道习题大概是ACM输入输出格式的普及 P1:---------------------------------------------------------------------------- ...

  7. A. 拼音魔法

    A. 拼音魔法 Time limit per test: 1.0 seconds Time limit all tests: 1.0 seconds Memory limit: 256 megabyt ...

  8. 安装Windows Azure Powershell

    本文将介绍如何安装Windows Azure Powershell 1.打开Azure官方链接:https://www.azure.cn/downloads/ 2.按照向导进行安装 3.打开系统自带的 ...

  9. C# linq创建嵌套组

    以下示例演示如何在 LINQ 查询表达式中创建嵌套组. 首先根据学生年级创建每个组,然后根据每个人的姓名进一步细分为小组. public void QueryNestedGroups() { var ...

  10. FTP配置的一些笔记

    1.必须关闭防火墙 iptables -F iptables -X iptables -Z vi /etc/selinux/config          SELINUX=disabled seten ...