Android的Message源码解读
实现了Parcelable ,可序列化的一个类。包含着要传递的信息。
1、常用的几个属性
int what | 消息的匹配code,用来区别不同的消息对应着不同的处理结果 |
int arg1/arg2 | Message携带的int信息,也可以通过setData设置 |
Object obj | Message携带的Object信息,也可以通过setData设置 |
Handler target | 该Message对应的Handler,用来进行分发消息 |
Runnable callback | 处理消息, |
2、常用的几个方法
(1)obtain()
从全局池中返回一个新的Message对象,避免重新分配一个新的对象。同时也可以根据不同的message参数创建出一个message对象来。其中这个全局池是一个链表。具体从全局池中怎么取出这个对象,可以参见下面的手绘图。
private static final Object sPoolSync = new Object();
private static Message sPool; //当前节点对应的Message的信息
public static Message obtain() {
synchronized (sPoolSync) {
//如果全局池不为空,则取当前最上面节点的,遵循先进后出原则
if (sPool != null) {
Message m = sPool;//当前message赋值给m,返回,并且next赋值为null
sPool = m.next;//sPool变更为当前message的下一个节点,
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;//最后就相当于删除了最后插入的节点
return m;
}
}
return new Message();
}
(2) recycle()
因为在回收的时候,是将当前message回收到全局池中。
message的全局池组成
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;
其中线程的并发处理是通过synchronized (sPoolSync)对sPoolSync进行加锁。
为什么这里不采用对象锁或者类锁呢?
由于静态方法,对象可能不存在,所以不可能用对象锁;
虽然类锁也可以实现,但是MessagePool逻辑和Message类本身关系不大,一方面不是很恰当,另一方面也限制了Message的拓展。
链表结构的实现
(aa) 加入链表
在MessageQueue移除Message的时候,都会调用到Message.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) {
//将当前的message插入到全局池
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
我们可以看到在加入到消息池的时候,首先需要将所有的字段都置空,避免Message过大,使静态线程池内存泄漏。所以这些缓存的对象所占的内存几乎可以忽略,并不会造成APP的OOM。
内置锁,只缓存50个,大于50的直接丢弃
分析下插入的过程,其实就是一个链表插入
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
//将当前的message插入到全局池
next = sPool;
sPool = this;
sPoolSize++;
}
}
当MessageQueue在removeMessage的时候,会执行到Message.recycleUnchecked()这个方法,然后就会按照左边图示的方式,将当前使用过的Message插入到链表中。最后生成的就是最后的链表结构。
(bb) 获取链表中的Message对象
另外在obtain()获取一个Message对象的时候,其实就是将全局池中的最上面的一个Message对象取出,可以参见上面上面手绘图的右半部分。
synchronized (sPoolSync) {
//如果全局池不为空,则取当前最上面节点的,遵循先进后出原则
if (sPool != null) {
Message m = sPool;//当前最上面节点的message赋值给m,返回,并且next赋值为null
sPool = m.next;//sPool变更为当前message的下一个节点,
m.next = null;//并且next赋值为null
m.flags = 0; // clear in-use flag
sPoolSize--;//最后就相当于删除了最后插入的节点
return m;
}
}
结合代码看下:
将最上面的节点取出赋值给返回的m对象,同时将sPool指向之前的上一个节点。注意返回的时候,还要将m上的next和flag都复位。