最近在学习Android消息机制方面的原理,到了Message这块的时候发现少有文章进行分析,本着“生死看淡不服就干”的原则,我深入了源码层面,对Message巧妙的复用机制进行了解析,并写下这篇文章,供大家分享学习,如果感觉对你有帮助,欢迎点赞,私信,关注我,您的鼓励是对我最大的帮助。
在日常的Android开发中,我们经常会使用Handler来进行主子线程之间的消息传递,其中我们用的最多的就是Message对象。通过查阅官方文档或者代码注释我们可以发现,对于Message,官方建议我们使用Message.obtain来进行Message对象的获取。
那么问题来了,为什么Google会建议我们使用Message.obtain来进行对象的获取而不是让我们自己new一个Message出来呢?或者说,通过Message.obtain来获取Message对象有没有什么好处呢?
其实答案都在源码里了,谈到这里,我们就不得不提一种设计模式——享元模式,这是一种和单例设计模式很相似的设计模式,具体的细节在这里就不讲了,感兴趣的话欢迎移步菜鸟教程——享元模式。
下面还是让我们从源码开始吧,Message复用原理的入口在Message.obtain中:
/**
从Message池中返回一个Message对象,并且允许分配新的实例
*/
public static Message obtain() {
//先是加了一把对象锁,这个锁是使用Object来实现的
synchronized (sPoolSync) {
if (sPool != null) {
//如果Message池中非空,就从池子里取出一个Message
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
//如果Message池为空,就新建一个Message对象出来
return new Message();
}
在obtain的代码中,我们可以看到,先是判断了一把Message Pool是不是为空,如果非空的话就从池子里取出之前用过的Message进行复用,并且异步地对池子里 Message的数量进行了更新。但是如果Message池为空的话说明还没有Message被放进去,那么就new出来一个Message对象好了。
到这里,仅仅是对之前使用过的Message进行了取出,或者新建一个Message的动作,还没有看到Message到底是怎么被回收的,别急,让我们移步Message的recycle方法。
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
//最终调用到了这个方法
recycleUnchecked();
}
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
这句话的意思是:一般来说这个方法是被内部的MessageQueue和Looper在分发排序的Message时候来使用的
*/
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
//先把这个Message标记为使用状态,防止被二次回收
//然后把这个Message的相关状态进行了手动复位
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
//Message的复用机制使用了链表数据结构来实现
//但神奇的是这个链表没有借助任何一种数据结构如LinkedList等实现,而是通过指针的形式,将不
//同的Message对象进行了串联
//在这里,先是判断了一把,如果当前Message池子的大小还没到限制值
//那么就把当前Message池子的头指向了当前Message实例的sPool字段,也就是Message池的头
//部,当然,next字段和Message池子都属于Message类型
//并且更新了池子的大小
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
从代码中我们可以看到,Message的复用机制没有使用任何一种数据结构,如LinkedArrayList,而是通过Message对象内部的spool和next字段,通过指针的方式来进行对象管理,不得不说,是一种非常巧妙的设计方式,一来降低了设计复杂度,而且由于没有创建额外的数据容器来管理对象,减轻了内存的压力,实现了轻量化的目的。
具体的实现过程我找到了一幅图来进行说明:
在Message池初始状态下,如果一个Message来到了池子里,他肯定先是被清空了数据,然后将他的sPool字段指向了Message池的头部,而next字段指向了空,当第二个Message被回收进来之后,m2的next字段指向了m1,原来m1的next字段仍然保持不变,而Message池子的头部也就是sPool指向了m2。
这样周而复始地循环,也就完成了Message的链表,进而实现了Message的复用目的。