同步屏障
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
在Message.next()方法中有这样一个逻辑,如果取出来的Msg不为空但是msg.target是空的,会不断的往后面遍历,找到第一个msg.isAsynchronous()为false的,isAsynchronous 这个是用来判断这个消息是否为同步消息,也就是说如果遇到msg != null && msg.target == null的情况,就会遍历找到第一个异步的消息进行处理,这种target为null的msg 也是handler发送的一个同步屏障,在enqueueMessage 这个消息入队的方法中有对target是否为空做判断,所以发送屏障是另外一个调的另外一个方法
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
设置同步屏障可以使得消息队列在获取消息时,如果遇到同步屏障,会忽略后面的同步消息,取出第一个异步的消息,可以使得一些消息优先执行。比如TraversalRunnable 消息就是异步消息,发送这个消息之前发送一个同步屏障Msg,可以使这个绘制的消息优先执行。
ThreadLocal
Handler 中的Looper 保存在ThreadLocal中
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
为什么要保存在ThreadLocal中呢?个人理解是,每个线程都会有自己的Looper 去获取Msg,而ThreadLocal是一个线程内部的存储类,可以在指定的线程保存数据和获取对应线程的数据,每个线程只能从ThreadLocal中访问到自己线程对应的数据,不能访问其他线程的,也就是线程A 只能访问到自己的Looper A,不能访问到线程B的Looper B。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
可以看到ThreadLocal保存数据的时候是先获取当前的线程,然后再根据当前线程t去获取实际保存数据的ThreadLocalMap
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
ThreadLcoalMap 是以ThreadLocal 作为key来保存value值。
阻塞唤醒机制
在MessageQueue 遍历消息的方法中有这样本地调用的代码
nativePollOnce(ptr, nextPollTimeoutMillis);
其中nextPollTimeoutMills 是用来描述当消息队列中没有新的消息需要处理时,当前线程需要进入的睡眠等待的时间,如果值为0,则表示即使当前没有消息要处理,当前线程也不要进入睡眠等待状态。如果值为-1,则表示没有消息处理时,当前线程需要无限的处于睡眠等待状态,直到有新的消息到来或者被其他线程唤醒。当前线程会在C++层创建一个epoll 实例,并且将它的文件描述符保存在C++层的Looper类的成员变量mEpollFd 中,同时还将一个管道的读端文件描述符注册到里面,监听这个管道的IO写事件,如果这些文件描述符没有发生IO读写事件,那么当前线程就会在函数epoll_wait 中进入睡眠等待状态。
参考
《Android 系统源代码情景分析(第三版)》