我们知道 Handler 可以发送2中消息:同步消息、异步消息。他们在处理方式上没有区别,只有当设置了同步屏障才会出现差异。
Handler 同步屏障
// MessageQueue.java
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
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;
}
}
可以看出,它是将消息按照时间顺序放入链表中。最大的不同观点在于这个消息没有设置 Handler。正常情况下,通过 Handler 的 post,sendEmptyMessage,sendMessage*** 等函数都是调用的 enqueueMessage:
//Handler.java
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
//...
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看出它设置了一个 Handler 到 Message 中,用于取出消息后通过这个 Handler 来处理。但是同步屏障的 Message 的 Handler 为空。
同步屏障的原理
同步屏障在 Looper 中循环获取消息的时候才会起作用,即在 MessageQueue#next 函数中发挥作用:
Message next() {
//...
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
//...
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//这里就会判断 Handler 是否为空,为空的话则说明是同步屏障
if (msg != null && msg.target == null) {
// 从内存屏障消息的下一个消息找起,找出第一个异步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
//...
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
//将这个异步msg从消息链表中移除,此时内存屏障还在
prevMsg.next = msg.next;
} else { // 移除内存屏障
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
//返回异步消息
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
//...
}
//...
}
}
可以看出整个过程中就是从内存屏障消息的后一个消息开始,查找异步消息。找到后将消息移除并返回。该消息执行完成后,由于同步屏障还在,继续查找同步屏障后的异步消息。如果整个链表后面没有异步消息了,则将同步屏障消息移除掉。
可以看出,同步屏障的作用是直接忽略所有的同步消息,然后找到异步消息发送出去。
## 如何发送异步消息
```java
public Handler(boolean async);
public Handler(Callback callback, boolean async);
public Handler(Looper looper, Callback callback, boolean async);
通常使用 handler 发送的都是异步消息,若要发送同步消息,则在构造 Handler 的时候需要将 async 参数设为 true。
为什么需要这样设计
主要是为了让一些优先级高的事情先做,譬如安卓应用框架中,为了加快相应 UI 刷新时间,将 scheduleTraversals 使用同步屏障,这样他就会得到优先执行。
参考文献
- 原子操作(CAS)
- jdk 1.8 源码
- NIO源码阅读(3)-ServerSocketChannel