前言
Message位于android/os/Message.java文件中
两个向消息池中添加(回收)一个Message对象的2个方法:recycle()和recycleUnchecked()方法
从消息池获取一个Message对象的方法:obtain()方法
今天一起学习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();
}
用于向消息池添加(回收)Message对象的方法,从消息池(Message对象池)的角度来看,就是回收一个Message对象,所以此方法命名为recycle非常贴切。
在方法的内部会根据当前Message对象的状态做出处理,正在消息池中的Message对象(可用状态)与未在消息池中的Message对象(不可用状态),当前Message对象处于可用状态时,才会进一步调用recycleUnchecked()方法进行Message对象的回收
第一:可用状态:首先执行了isInUse()方法,该方法返回当前Message对象的是否为可用状态,isInUse()方法返回的是当前Message对象可用的情况下,说明Message对象在消息池中可使用,且gCheckRecycle标志位是开启状态(API版本大于等于21是开启状态)的话,就直接抛出IllegalStateException对象,用于告知调用者,Message对象是可用状态,不能再次缓存,因为它正在消息池中,当gCheckRecycle标志位未开启(API版本小于21是未开启状态),方法会执行到return语句,整个方法结束,当前Message对象也不会被缓存
第二:不可用状态:当前Message对象为不可用状态,说明已经从消息池中取出来使用过了,则会进行到recycleUnchecked()方法,该方法会实际执行缓存Message对象
isInUse()方法分析
boolean isInUse() {
return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
}
返回Message对象的可用状态,true表示Message对象为可用状态(即正在消息池中),false表示Message对象为不可用状态(已经从消息池中取出来使用了),举个例子
00000001 flags
00000001 FLAG_IN_USE
按位与结果为
00000001
说明当前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) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
这是真正回收Message对象(缓存Message对象)的地方,要把一个对象初始化为缓存对象,要做两件事情
第一:为当前Message对象持有的标记flags赋值为可用状态,说明可以在消息池中被去取出来使用,继续为Message对象中持有的实例变量what赋值为0,arg1为0,arg2为2,ojb指向null,replyTo指向null,sendingUid赋值为-1,when赋值为0,target赋值为null,callback赋值为null,data赋值null,可见当前Message对象持有的属性全部赋值为零值,就是当下的第一件事,这样当前的Message对象就和通过new创建一个新的Message对象的状态是一样的!
第二:已经初始化为零值状态的当前Message对象会被添加到一个单链表中作为头结点,在缓存过程中,当前执行缓存任务的Thread对象需要先持有sPoolSync对象对应的锁,这是为了确保三个共享变量next、sPool、sPoolSize在多线程下均可以被正确写入,若同一时刻有多个线程写入三个共享变量,则三个共享变量的值会被线程间相互覆盖掉,判断当前已经缓存的Message对象数量是否小于最大的缓存对象总数50,小于获得指向第一个Message对象的sPool变量,sPool变量是单链表的头结点引用,它总是指向单链表中的第一个Message对象,然后sPool先将自己目前持有的头结点Message对象赋值给当前Message对象的next实例变量负责持有上,接着sPool指向当前Message对象,最后再将消息池的总数+1,就完成了当前Message对象的缓存工作
消息池相关的字段介绍(Message类持有的与消息池相关的类变量与常量)
public static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50; 消息池存储的Message对象最大数量
private static boolean gCheckRecycle = true; 标记当前是否需要检查Message对象正在使用
Message类持有的Object对象oPoolSync(常量),作为线程间同步使用的对象锁(注:sPoolSync的访问权限是public权限(对我们是hide))
Message类持有的Message对象sPool,永远指向的是单链表的第一个Message对象(头结点对象)(实现缓存Message对象的消息池,使用的是单链表),因为sPool是类变量,它位于方法区,与class的生命周期一样,为此会阻止GC回收单链表中的每一个对象,以达到内存缓存的目的
Message类持有的int变量sPoolSize,代表消息池(单链表)中缓冲的Message对象的数量,即实际缓存结构单链表的元素总数
Message类持有的常量MAX_POOL_SIZE,代表的是消息池最大可缓存的Message对象的数量,这里是50
Message类持有的标志位gCheckRecycle,代表是否需要检查Message对象的可用状态
FLAG_IN_USE常量介绍
/*package*/ static final int FLAG_IN_USE = 1 << 0;
用于标记Message对象的状态,1左移0位,仍是二进制……省略000000001
从消息池的角度来看,这个状态表示两个意思
可用状态:表示Message对象正在消息池中,可取出使用(说明不可回收)
不可用状态:表示Message对象已经从消息池中,被取出来使用(说明可回收)
updateCheckRecycle()方法分析
/** @hide */
public static void updateCheckRecycle(int targetSdkVersion) {
if (targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
gCheckRecycle = false;
}
}
hide权限的方法,用于更新gCheckRecycle标志位,在目标SDK小于API21时会更新为false,gCheckRecycle标记是否需要检查Message对象使用状态的标志位,目前不知道在哪里调用
public static final int LOLLIPOP = 21;
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();
}
用于获取一个Message对象的方法,原来它是从Message类持有的消息池sPool中取出一个Message对象供我们使用的,这是google推荐使用该方法的原因!详细分析见第一篇文章
总结
1、一个用户进程中,会使用无数个Message对象,将使用过的Message对象在内存中缓存起来,后面复用时只需改变对象持有的属性(实例变量)即可,复用对象可以节约内存空间,也可以节约创建对象的性能开销,还可以减少GC的工作,这是官方推荐我们使用obtain()系列方法获取Message对象的真正原因,牛B!
2、Message类持有作为消息池头引用的静态变量sPool,消息池是以单链表结构进行实现的,sPool是一个静态变量,它引用的对象的生命周期与类的生命周期一样长,这样处于单链表中的每一个Message对象均不会被gc回收,所以它们可作为内存缓存使用
3、消息池(单链表)最大可以缓存50个Message对象,也就是说单链表的最大长度为50个元素
4、当缓存的Message对象总数已经为50个时,不能再被缓存的Message对象会被gc回收掉
5、保留在消息池(单链表)中的Message对象,都被标记成了FLAG_IN_USE,表示Message对象是可用状态
6、在消息池中缓存Message对象、获取Message对象,均是在头结点处完成的,传说中的头插法,即在头部插入元素、也在头部取出元素,这是单链表最高效的插入与删除了,时间复杂度O(1),运用的牛逼!
7、缓存对象时,将Message对象的实例变量均赋值为零值(初始值),牛BBBBB