Android消息机制

同步屏障

博客: Handler同步屏障机制的原理和使用场景_u013728021的博客-CSDN博客

Android中同步屏障的应用及简析 - 简书 (jianshu.com)

什么是同步屏障:

         Handler的消息分为两种:同步消息和异步消息。

1
2
3
4
5
6
7
8
9
10
11
12
//Message.java 获取这个消息属于哪种
public boolean isAsynchronous() {
return (flags & FLAG_ASYNCHRONOUS) != 0;
}
//Message.java 设置这个消息类型
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}

这两种消息在常规使用中毫无不同之处。但是当使用同步屏障后就不同了。

设置使用同步屏障后,只会获取和分发Looper里面的异步消息,如果没有异步消息,就会进入阻塞。

当设置了同步屏障之后,next函数将会忽略所有的同步消息,返回异步消息。换句话说就是,设置了同步屏障之后,Handler只会处理异步消息。再换句话说,同步屏障为Handler消息机制增加了一种简单的优先级机制,异步消息的优先级要高于同步消息。

如何使用

  1. 通过MessageQueue的postSyncBarrier方法在链表头部插入一个target = null 的Message
  2. 发送一个异步消息
    1. 1 发送异步消息的方式:Message.setAsynchronous(true)

设置同步屏障

通过MessageQueue里的postSyncBarrier函数设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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;
}
}
// 下面这操作就是将target=null的message插到头部
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg; // 将当前msg设置给mMessages
}
return token;
}
}

方法分析:这个函数创建了一个Message对象然后插入到了消息列表头部的位置,这个消息的最大特征就是他的target为null。我们普通发的消息,在底层都设置了target的值。

1
2
3
4
5
6
//Handler.java
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
//...
return queue.enqueueMessage(msg, uptimeMillis);
}

总结:

同步屏障就是需要通过MessageQueue里的postSyncBarrier方法添加进入一个target字段为空的Message。

同步屏障的应用

View的创建: 在ViewRootImpl的requestLayout开启绘制机制的时候:

1
2
3
4
5
6
7
8
9
10
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();//检查是否在主线程
mLayoutRequested = true;//mLayoutRequested 是否measure和layout布局。
//重要函数
scheduleTraversals();
}
}

Android框架为了更快的响应ui刷新事件在ViewRootImpl.scheduleTraversals中使用了同步屏障。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//设置同步障碍,确保mTraversalRunnable优先被执行
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//内部通过Handler发送了一个异步消息
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}

mTraversalRunnable 调用了 performTraversals 执行measure、layout、draw

为了让mTraversalRunnable尽快被执行,在发消息之前调用MessageQueue.postSyncBarrier设置了同步屏障。