本篇主要谁理解Android Handler的Message类。
Message的作用
首先从Message的注解开始。
Defines a message containing a description and arbitrary data object that can be sent to a
{@link Handler}. This object contains two extra int fields and an extra object field that allow
you to not do allocations in many cases.
While the constructor of Message is public, the best way to get one of these is to call
{@link #obtain Message.obtain()} or one of the {@link Handler.obtainMessage
Handler.obtainMessage()} methods, which will pull them from a pool of recycled objects.
翻译如下:
定义一个可以发送给Handler的包含描述和任意消息对象的消息。此对象包含两个额外的int字段和一个额外的object字段,这样允许你在很多情况下不用做分配工作。尽管Message的构造器是public,但是获取Message对象最好的方法是通过Message.obtain()或者Handler.obtainMessage()方法,这样可以从一个可回收的对象池中获取Message对象。
Message重要的成员变量
/**
*用户定义的message辨识符,因此接收者可以分辨消息的内容。
*每个Handler都有自己的消息代码的命名空间,因此你不用担心和其他Handler产生冲突。
*/
public int what;
/**
*如果你仅仅需要保存一些简单的int类型数值
* 这两个变量是用来代替setData()的低成本可选择的方案
*/
public int arg1;
public int arg2;
/**
*发送给接收者的任意对象。
* 当通过程序,使用Messenger发送消息时,如果这个对象包含Parcelable类,
* 那它必须是非空的。
* 对于其他的数据传输,使用setData()方法
*/
public Object obj;
/**
*用来存储比较复杂的数据
*/
Bundle data;
/**
*用来存储当前Message的Handler
* Handler和Message是相互持有引用的关系
*/
Handler target;
/**
* 指向下一个Message,也就是线程池是一个链表结构
*/
Message next;
/**
* 该静态Message是整个线程池链表的头部
*/
private static Message sPool;
/**
* 记录线程池中Mesage的数量,也就是链表的长度
*/
private static int sPoolSize = 0;
Message的构造方法
public Message() {
}
可以看到,构造方法里面什么也没有,前面说过,获取Message对象的首选方法是通过Message.obtain()。
下面看下Message.obtain()方法。
Message.obtain()
看一下Message.java类的结构图
Message居然有8个obtain方法,我们以最常用的不带参数的obtain()方法为例。
/**
* 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;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
看一下这个方法的注释,从全局的pool返回一个实例化的Message对象,这样可以让我们避免创建冗余的对象。
注释里提到一个pool,通常理解为池,看到代码里有一个变量sPool,这里就涉及到了Message的设计原理。下面先看一下sPool。
sPool
先看一下sPool的定义:
private static Message sPool;
原来sPool就是一个Message对象而已,默认是null。
Message.java正是通过sPool,来获取Message对象和回收Message对象,避免重复创建冗余的Message对象。
全局看一下sPool的赋值情况,可以看到sPool除了在obtain()方法里赋值意外,还在recycleUnchecked()方法里赋值,下面就把这两个方法放在一起分析,理解Message的对象池概念。
recycleUnchecked()
recycleUnchecked()回收Message对象。
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
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++;
}
}
}
在recycleUnchecked()代码中,前面一段代码主要是清除Message对象的信息的,后面的关键代码才是回收Message对象的。
深入理解消息对象池
我们把recycleUnchecked()和obtain()合在一起,省略一些不重要的代码
代码如下:
void recycleUnchecked() {
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
// 第一步
next = sPool;
// 第二步
sPool = this;
// 第三步
sPoolSize++;
}
}
}
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();
}
recycleUnchecked()的理解
刚开始sPool为空,所以在首次获取Message对象时,直接通过 new Message()方式返回Message对象,当这个Message对象被使用后,要通过recycleUnchecked()方法回收。看一下recycleUnchecked()方法。
- 第一步,next = sPool,next前面说了,指向下一个message,因为此时sPool=null,所以这一步将指向下一个message的next赋值为null。
- 第二步,sPool=this,将当前这个message作为消息对象池中下一个被复用的对象。
- 第三步,sPoolSize++,sPoolSize默认为0,现在为1,sPoolSize的长度代表线程池中message的数量。
整个流程可以用下面的图表示:
这是线程池中只有一个message的情况。
假设线程池中已经存在了一个message1,继续走上面的流程。
- 第一步,next=sPool,此时消息对象池为message1,所以此时sPool为message1,经过这一步,next为message1。
- 第二步,sPool=this,将当前的message作为消息对象池中下一个被复用的对象。
- 第三步,sPoolSize++,此前sPoolSize为1,现在为2,sPoolSize的长度代表线程池中message的数量。
以此类推,直到sPoolSize=50(MAX_POOL_SIZE = 50)。
整个流程可以如下表示:
obtain()的理解
刚开始,sPool为空,所以要获取message时,直接new一个返回。
假设message使用完,上面已经回收了一个Message对象,现在又从obtain()方法里获取一个Message对象。
- 第一步,判断sPool是否为空,如果消息对象池为空,则直接new Message返回。
- 第二步,sPool不为空,Message m = sPool,将消息对象池的message取出来,记为m。
- 第三步,sPool = m.next,将消息对象池中下一个message对象赋值为消息对象池中的当前可以复用的对象。如果此时消息对象池中只有一个可以复用的message对象,则此时sPool = null。
- 第四步,m.next = null,此时这个message对象已经取出来了,它指向的下一个message对象为null。
- 第五步,m.flags = 0,设置m的标记位,标识此message对象正在被使用。
- 第六步,sPoolSize--,messgae已取出,对象池的长度要减1。
整个流程大体如下:
总结
以上就是Message的设计原理,Message对象获取和Message对象回收,都是通过消息对象池的方式,避免了重复重建大量的对象,因此内存泄漏。
网友评论