学习Handler之后,通常会出现同步屏障的字样。MessageQueue管理优先级队列的过程中,如果消息存在一种“紧急”消息, 需要更高的优先级处理,这个时候就需要同步屏障。

Handler3 同步屏障

开篇问题

1.为什么会存在同步屏障

2.如何去除同步屏障

3.同步屏障有啥用

什么是同步屏障

同步屏障就是阻碍同步消息,只让异步消息通过

Message分为3中:普通消息(同步消息)、屏障消息(同步屏障)和异步消息。我们通常使用的都是普通消息,而屏障消息就是在消息队列中插入一个屏障,在屏障之后的所有普通消息都会被挡着,不能被处理。不过异步消息却例外,屏障不会挡住异步消息,因此可以这样认为:屏障消息就是为了确保异步消息的优先级,设置了屏障后,只能处理其后的异步消息,同步消息会被挡住,除非撤销屏障。

注:没有同步屏障,不区分同步消息和异步消息,都属于一般消息按时间优先级来发送

如何发送异步消息

1Message message = Message.obtain();
2message.setAsynchronous(true);
3handler.sendMessage(message);

同步屏障原理

开启同步屏障

 1MessageQueue.java#postSyncBarrier()
 2/*
 3 * @hide
 4 */
 5public int postSyncBarrier() {
 6    return postSyncBarrier(SystemClock.uptimeMillis());
 7}
 8
 9private int postSyncBarrier(long when) {
10    // Enqueue a new sync barrier token.
11    // We don't need to wake the queue because the purpose of a barrier is to stall it.
12    synchronized (this) {
13        final int token = mNextBarrierToken++;//类似句柄,用于管理当前的屏障的唯一标识
14        //此处跟Handler.enqueueMessage区别是这里没有msg.target
15        final Message msg = Message.obtain();
16        msg.markInUse();
17        msg.when = when;
18        msg.arg1 = token;
19
20        Message prev = null;
21        Message p = mMessages;
22        //将屏障根据时间插入链表动作
23        //开启同步屏障时间T不为0,且当前的同步消息里有时间小于T,则prev也不为null
24        if (when != 0) {
25            while (p != null && p.when <= when) {
26                prev = p;
27                p = p.next;
28            }
29        }
30        if (prev != null) { // invariant: p == prev.next
31            msg.next = p;
32            prev.next = msg;
33        } else {
34            msg.next = p;
35            mMessages = msg;
36        }
37        return token;
38    }
39}

开始对消息同步屏障

 1MessageQueue.java#next()
 2Message next() {
 3    ...
 4    for (;;) {
 5        ...
 6		//nextPollTimeoutMillis
 7        //1.取值为-1,一直阻塞不会超时,mMessages为空,知道下一次唤醒
 8        //2.取值为0,不会阻塞,当前时间和下一个消息时间相同,立即处理
 9        //3.取值为大于0,最长阻塞为nextPollTimeoutMillis(超时机制),一般是当前时间早于下一个消息
10        nativePollOnce(ptr, nextPollTimeoutMillis);
11
12        synchronized (this) {
13            // Try to retrieve the next message.  Return if found.
14            final long now = SystemClock.uptimeMillis();
15            Message prevMsg = null;
16            Message msg = mMessages;
17            //同步屏障起作用
18            //判断除了第一个同步屏障之后的第一个非同步消息出现,退出循环
19            if (msg != null && msg.target == null) {
20                // Stalled by a barrier.  Find the next asynchronous message in the queue.
21                do {
22                    prevMsg = msg;
23                    msg = msg.next;
24                } while (msg != null && !msg.isAsynchronous());
25            }
26            if (msg != null) {
27                //同步消息和异步消息都需要判断是否需要设置阻塞时间
28                if (now < msg.when) {
29                    // Next message is not ready.  Set a timeout to wake up when it is ready.
30                    //用于计算上面的下一循环nativePollOnce阻塞时间
31                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
32                } else {
33                    // Got a message.
34                    mBlocked = false;
35                    //删除节点操作
36                    if (prevMsg != null) {
37                        prevMsg.next = msg.next;
38                    } else {
39                        mMessages = msg.next;
40                    }
41                    msg.next = null;
42                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
43                    msg.markInUse();
44                    return msg;
45                }
46            } else {
47                // No more messages.
48                //用于复位,没有消息,等待下一次唤醒
49                nextPollTimeoutMillis = -1;
50            }
51            ...
52    }
53}

关闭/移除同步屏障

 1MessageQueue.java#removeSyncBarrier()
 2/*
 3 * @hide
 4 */
 5//token为上述说明用于管理当前的屏障的唯一标识
 6public void removeSyncBarrier(int token) {
 7    // Remove a sync barrier token from the queue.
 8    // If the queue is no longer stalled by a barrier then wake it.
 9    synchronized (this) {
10        Message prev = null;
11        Message p = mMessages;
12        //找到当前屏障
13        while (p != null && (p.target != null || p.arg1 != token)) {
14            prev = p;
15            p = p.next;
16        }
17        if (p == null) {
18            throw new IllegalStateException("The specified message queue synchronization "
19                                            + " barrier token has not been posted or has already been removed.");
20        }
21        final boolean needWake;
22        //从链表中移除
23        if (prev != null) {
24            prev.next = p.next;
25            needWake = false;
26        } else {
27            mMessages = p.next;
28            needWake = mMessages == null || mMessages.target != null;
29        }
30        //Message的sPool对象池回收消息
31        p.recycleUnchecked();
32
33        // If the loop is quitting then it is already awake.
34        // We can assume mPtr != 0 when mQuitting is false.
35        //移除屏障,开始唤醒
36        if (needWake && !mQuitting) {
37            nativeWake(mPtr);
38        }
39    }
40}

用途

Android-Choreographer工作原理

问题回答

1.为什么会存在同步屏障

2.如何去除同步屏障

3.同步屏障有啥用

参考文献

[1] maove, Handler机制——同步屏障, 2019.

[2] willwaywang6, Android筑基——可视化方式理解 Handler 的同步屏障机制, 2020.

[3] 苍耳叔叔, Android-Choreographer工作原理, 2020.

猜你想看

Handler1 Looper

Handler2 Thread

Handler4 HandlerThread

Handler5 IntentService