由Handler、MessageQueue、Looper构成的线程消息通信机制在Android开发中非常常用,不过大部分人都只粗浅地看了Java层的实现,对其中的细节不甚了了,这篇博文将研究Android消息机制从Java层到Native层的实现。
消息机制由于更贴近抽象设计,所以整个结构更简单,只包含了消息的产生、分发,不像Input子系统那样还有归类、过滤等环节。整体的结构如下图:
Android Java层消息机制
消息的产生
在Java层中消息的产生都来源于用户创建的Message对象,经过封装的Runnable对象,或调用obtainMessage从Message Pool中获得,Message Pool指的是Message类内的Message循环队列,队头是静态的Message对象sPool,该队列最大容纳MAX_POOL_SIZE(50)个Message:
MessagePool对Message的复用节省了不断创建Message带来的开销,如果当前50个Message都已经被用过,由于MessagePool是循环队列,则会回到队头并请空该Message,向下复用。
BlockingRunnable
看Java层Handler的源码的时候发现了一个奇怪的东西:BlockingRunnable,基本上没有用过的东西,也没看别人讲过,于是我就来钻研一下吧:
private static final class BlockingRunnable implements Runnable {
private final Runnable mTask;
private boolean mDone;
public BlockingRunnable(Runnable task) {
mTask = task;
}
@Override
public void run() {
try {
mTask.run();
} finally {
synchronized (this) {
mDone = true;
notifyAll();
}
}
}
public boolean postAndWait(Handler handler, long timeout) {
if (!handler.post(this)) {
return false;
}
synchronized (this) {
if (timeout > 0) {
final long expirationTime = SystemClock.uptimeMillis() + timeout;
while (!mDone) {
long delay = expirationTime - SystemClock.uptimeMillis();
if (delay <= 0) {
return false; // timeout
}
try {
wait(delay);
} catch (InterruptedException ex) {
}
}
} else {
while (!mDone) {
try {
wait();
} catch (InterruptedException ex) {
}
}
}
}
return true;
}
}
我们可以看到,BlockingRunnable是一个“包裹”构造方法中传入的Runnable的Runnable,调用BlockingRunnable的postAndWait会做以下事情:
- 如果投递BlockingRunnable失败,返回false
- 锁住投递BlockingRunnable的线程
- 如果timeout大于0,计算参数Runnable的到期时间,只要参数Runnable还没处理完,则一直轮询还剩多少时间,并调用wait(delay)让投递BlockingRunnable的线程继续等待,直参数Runnable处理完(mDone为true)这个过程才结束
- 如果timeout小于等于0,而且参数Runnable还没处理完,则一直等待直到参数Runnable处理完(mDone为true)
这个东西的说明书和使用风险可以在runWithScissors方法的注释里看到,我在这里就不当翻译工了。
消息的投递和处理
得到Message后,就会通过Handler的sendMessageAtTime调用MessageQueue的enqueueMessage将Message投递到MessageQueue中,在往下学习之前必须先了解Handler的创建,因为后面的知识和它有关联。
Handler的创建和初始化
其实Handler的初始化没什么好看的,就是保存Callback、mLooper的MessageQueue的引用,以及声明Handler是否异步投递所有Message。但是里面有一个内存泄露的检查,可以学习一下,就是如果打开了FIND_POTENTIAL_LEAKS,就会进行内存泄露的检查,它会做以下事情:
- 获取当前Handler类
- 如果Handler是匿名内部类,或成员类,或局部类,且Handler的修饰符不是stat