一、前言
在Handler
当中,有一个很隐秘的成员变量mAsynchronous
,它是通过构造函数来传入的,但是该构造函数是hide
,说明它是留给系统的内部接口调用的,该标志位默认情况下为false
,也就是说平时我们通过Handler
发送的消息都为 同步消息。
根据注释的描述,当该标志位为true
时,那么我们通过Handler
发送的Message
时,会将Message
的setAsynchronous
方法。
也就是说创建异步消息的途径有两种:
- 构造函数初始化
Handler
传入true
,那么该Handler
发送的所有消息都为异步消息。 - 在创建
Message
时,调用setAsynchronous(true)
。
二、同步屏障
在简单地解释了mAsynchronous
的背景后,再先来介绍一下同步屏障的概念,只有设置了同步屏障,异步消息才会产生作用。插入同步屏障需要调用postSyncBarrier.postSyncBarrier(long when)
方法。
private int postSyncBarrier(long when) {
synchronized (this) {
//屏障的令牌号。
final int token = mNextBarrierToken++;
//获得一个空闲的 Message 对象。
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) {
msg.next = p;
prev.next = msg;
//无法找到前驱节点,那么将该消息作为首节点。
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
简单来说,就是在MessageQueue
维护的链表当中插入了一个特殊的Message
,该Message
的特点是其target
为空。
三、消息处理
MessageQueue
中的所有消息都是通过Looper.next()
来处理的。
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//阻塞式地获取消息。
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//如果获取到的是一个同步屏障消息。
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
//找到第一个异步消息才会退出该循环。
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
//...
}
//...
}
}
从上面的逻辑可以看出,当设置了同步屏障之后,会忽略所有的同步消息,优先处理异步消息,当我们希望某个任务可以优先执行时,就可以采用这种策略。
四、应用
在ViewRootImpl.scheduleTraversals
中就用到了该策略来优先执行mTraversalRunnable
的刷新UI
操作。
网友评论