大家都知道Android应用程序是通过消息来驱动的。每开启一个应用进程,我们都会在当前应用进程的主线程成创建一个消息队列,和Handler来处理应用程序中的消息。不管是刷新界面,或者是启动组件等等,都和消息相关。所以Message对象在我们的应用程序中使用时很频繁的。
一般情况下,我们会通过Handler去发送一个消息,而这个消息(即Message对象)我们可以通过关键字new进行创建,也可以通过Message.obtain()方法进行创建。在Android源码中,我们在Message的构造方法上方可以看到如下一段注释:
/** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
*/
public Message() {
}
这段注释的意思是:更加建议我们使用Message.obtain()方法来获取一个Message对象。
为什么Android官方更加建议我们使用Message.obtain()方法呢?下面我将从源码角度,带大家了解一下其中的奥妙。
既然它建议我们使用obtain()方法,那我们就来看看它为我们做了些什么事。
以下源码都源码基于Android8.0。
1.解析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不为null的时候,还是会调用new Message()来创建一个Message对象。那么这个sPool又是个什么东西呢?其实呢从上面代码我们就可以看出来,它其实是一个链表。每个Message对象都有next字段,它的类型也是Message。上面的代码其实就是把链表中的第一个节点返回,然后把sPool置为第一个Messgae的next对象,即下一个节点。那我们这个sPool对象是怎么生成的呢?我们在来找一找答案。我们在Message类中搜索一下sPool这个对象,看看什么时候会往它里面添加Message对象。我们可以找到recycleUnchecked()方法,recycleUnchecked()是在recycle()方法中被调用的。
2.解析recycle()方法和recycleUnchecked()方法
public void recycle() {
//判断当前Message对象是否能被回收
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
//可以回收调用recycleUnchecked()方法
recycleUnchecked();
}
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
//清除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;
synchronized (sPoolSync) {
//当sPoolSize大小小于MAX_POOL_SIZE即50时,继续往池中添加。
//sPoolSize可以理解为Message池内Messgae对象的数量。
if (sPoolSize < MAX_POOL_SIZE) {
//当前Message对象当作第一个链表的第一个节点
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
好了现在我们知道了当Message对象被回收的时候,会像池中添加一个Messgae对象。那么什么时候会回收一个Messsge对象呢。可以猜想以下,一个对象不用了之后才会被回收,在Android消息机制中,Looper会通过loop()方法想消息队列中去消息,我们去loop()方法中找找,看看是否会调用我们Message的回收方法。
3.解析Looper.loop()方法
源码如下:
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
boolean slowDeliveryDetected = false;
//无限循环
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//省略部分代码
.....
.....
try {
//调用Handler的dispatchMessage来处理消息
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//此处省略部分代码
.....
.....
//调用回收处理完成的Message的方法
msg.recycleUnchecked();
}
}
可以清楚的看到当我们的Looper对象处理完我们的Message时,会去调用回收Message的方法,向我们的Message对象中的复用池中添加一个Message对象。
4.总结
分析到这里,我相信所有朋友都明白了。Message类里维护了一个sPool对象。可以理解成一个Message链表,这个链表默认的最大长度是50。在Android消息机制中,每当一个Message对象被处理完成之后,都会被放入这个池中,为我们提供了复用。当我们调用Message.obtain()方法时,如果复用池中存在Message对象,我们就不会去创建一个新的Message对象。这样就避免了频繁创建和销毁Messgae对象的带来的性能开销。