开篇:当大家学会使用
Handler
之后,有经验的前辈就开始提示说,不要直接使用new Message()
的方式去创建Message
对象,而应该使用Message.obtain()
的方式去获取一个Message
对象,这样可以减少Message
对象的创建,节省内存。
为什么通过Message.obtain()
的方式获取Message
对象就能节省内存呢?系统对Message
对做了缓存?那又是如何做的缓存呢?这里的缓存会存在内存泄漏的问题么?
Message变量
首先Message
类有下面几个重要的变量,系统是通过这几个变量来实现Message
的缓存与复用的,不用着急,先记住这几个变量即可,注意这几个变量的特点,sPool
和sPoolSize
是静态变量,next
是一个普通变量,MAX_POOL_SIZE
是一个值为50的静态常量。看下这个结构,再联想下链表的结构,是不是有点意思。
public final class Message {
/*package*/ Message next;
/** @hide */
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
}
recycle
接下来看下Message
的recycle
方法,当一个消息被处理完后,该消息也就是对应的Message
对象的recycle
方法会被调用,即回收Message
对象。
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
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++;
}
}
}
recycle
方法首先检查了该Message
是否正在使用,如果正在使用的
Message
对象调用该方法,会直接抛出异常,很明显,未处理过的消息,是不应该被缓存的。
然后实际的消息回收操作是在recycleUnchecked
方法中执行的,recycleUnchecked
方法首先将当前消息的部分成员变量的值置为默认值,然后将静态变量sPool
赋值给了变量next
,静态变量sPool
则记录了当前的Message
,同时sPoolSize+1
,MAX_POOL_SIZE
的值是50,即这里最多缓存50个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();
}
通过obtain
源码可以看如果sPool
非空则返回到返回sPool
,同时将sPool
的值设为next
,且sPoolSize-1
。如果
sPool
为空,则创建一个新的Message
对象返回。
总结
Message
消息被消费后,对应的Message
对像被清除内容信息,然后通过类似链表的结构缓存下来,通过obtain
获取Message
对象,是从“链表”中取出缓存的Message
对象,即静态变量Message.sPool
的记录的值。因为缓存Message
对象时对Message
对象中的“消息内容”做了清理,所以不会因为缓存的Message
对象携带大的对象而导致内存泄露。从下图中更容易理解整个过程。