1, the introduction

As we know, The Android messaging mechanism is between Handler, Looper, Message, and MessageQueue. This article assumes that you are familiar with them, so it is not intended to introduce the myriad of links between them. For those who are not familiar with them, please refer to other blog posts on the Internet

There’s a little detail that a lot of people probably haven’t noticed, and that’s what’s the synchronization barrier for the messaging mechanism? What does synchronization barrier have to do with target == NULL? What’s the difference with target not null? The purpose of this article is to expose the subtle relationship between synchronization barriers and Target.

2. What is target

First, take a look at where target is defined in the Message class:

//Message.java

Handler target;
Copy the code

A Message holds a Handler. A target is a Handler object.

Now, where does Target come from?

Handle #enqueueMessage(); Handle #enqueueMessage();

 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
Copy the code

See, when we send a message, msg.target is assigned to this, and this is our Handler object. Therefore, the target of a message sent in this way must not be null, and mAsynchronous defaults to false, which means that we send synchronous messages. Conversely, there should be asynchronous messaging, right? Indeed, there is another type of asynchronous messaging that is easily overlooked because we rarely use asynchronous messaging outside of the system’s source code. So what is asynchronous messaging? A quick conclusion: a message that satisfies target == NULL is an asynchronous message. So how do you send an asynchronous message?

In short, there are two ways.

One is to set the message to be asynchronous directly:

Message msg = mMyHandler.obtainMessage();
msg.setAsynchronous(true);
mMyHandler.sendMessage(msg);
Copy the code

There is also a constructor that uses Handler, but it is marked @hide:

/**
  *
  * @hide
  */
  public Handler(boolean async) {
     this(null, async);
  }
Copy the code

Use as follows:

Handler mMyHandler = new Handler(true);
Message msg = mHandler.obtainMessage();
mMyHandler.sendMessage(msg);
Copy the code

The async argument passes true for an asynchronous message.

Note, however, that messages sent through the above two methods are not yet asynchronous messages, because they still end up in enqueueMessage() and still assign a value to target, resulting in target not being null. This is the same as synchronizing messages mentioned earlier. So when does target == null satisfy the condition?

Sync Barrier.

What is the synchronization barrier

Yes, the key to sending asynchronous messages is that the messages open a synchronization barrier. A barrier stands for block, and as the name suggests, a synchronous barrier blocks synchronous messages, allowing only asynchronous messages to pass through. How do you turn on the synchronization barrier? As follows:

MessageQueue#postSyncBarrier()
Copy the code

Let’s take a look at the dark arts behind this line of code:

   /**
     *
     * @hide
     */
    public int postSyncBarrier() {
        returnpostSyncBarrier(SystemClock.uptimeMillis()); } private int postSyncBarrier(long when) { // Enqueue a new sync barrier token synchronized (this) { final int token = mNextBarrierToken++; // Obtain Message final Message MSG = message.obtain (); msg.markInUse(); // Here it is!! Target ==null MSG. When = when; msg.arg1 = token; Message prev = null; Message p = mMessages;if(when ! = 0) {while(p ! = null && p. hen <= when) {// If T is not 0 and some time in the current synchronization message is less than T, then prev is not null prev = p; p = p.next; }} / Insert MSG chronologically to the appropriate position in the message queue (linked list), depending on whether prev is nullif(prev ! = null) { // invariant: p == prev.next msg.next = p; prev.next = msg; }else {
                msg.next = p;
                mMessages = msg;
            }
            returntoken; }}Copy the code

As you can see, the Message object is initialized without assigning a value to target, so the source of target == null is found. The insertion of the message above is commented accordingly. Thus, a message with target == NULL enters the message queue.

So how are so-called asynchronous messages handled once the synchronization barrier is turned on?

If you know anything about the message mechanism, you should know that the final processing of the message is in the message poller Looper#loop(), and the loop() loop calls MessageQueue#next() to fetch the message from the message queue. Here’s the key code:

//MessageQueue.java Message next() ..... Int pendingIdleHandlerCount = -1; // -1 only during first iteration // 1. If nextPollTimeoutMillis=-1, the block does not time out. // 2. If nextPollTimeoutMillis=0, it will not block and return immediately. If nextPollTimeoutMillis>0, block nextPollTimeoutMillis for milliseconds (timeout). int nextPollTimeoutMillis = 0; // Next () is also an infinite loopfor (;;) {
            if(nextPollTimeoutMillis ! = 0) { Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis); Synchronized (this) {final long now = systemclock. uptimeMillis(); synchronized (this) {final long now = systemclock. uptimeMillis(); Message prevMsg = null; Message msg = mMessages; // The head of the current list // key!! // If target==null, it is a barrier that needs to be looped back to find the first asynchronous messageif(msg ! = null && msg.target == null) { // Stalled by a barrier. Find the next asynchronous messagein the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while(msg ! = null && ! msg.isAsynchronous()); }if(msg ! = null) {// If there is a message to be processed, first check whether the time is up, if not, set the blocking time, // scenario such as common postDelayif(now < msg.when) {// Calculate how long until execution time to assign to nextPollTimeoutMillis, NextPollTimeoutMillis = (int) math.min (MSG. When - now, Integer.MAX_VALUE); }else{// The message mBlocked = was obtainedfalse; // List operation, get MSG and delete the nodeif(prevMsg ! = null) prevMsg.next = msg.next; }else{ mMessages = msg.next; } MSG. Next = null; msg.markInUse(); // Return the received messagereturnmsg; }}else{// No message, nextPollTimeoutMillis reset nextPollTimeoutMillis = -1; }... / / omit}Copy the code

As can be seen from the above, when the synchronization barrier is enabled on the message queue (marked as msg.target == NULL), the message mechanism preferentially processes asynchronous messages. In this way, synchronization barriers act as filters and prioritizers.

Here is a simple illustration with a schematic diagram:

As shown in the figure above, there are synchronous and asynchronous messages (yellow) and a wall —- synchronization barrier (red) in the message queue. With synchronization barriers, msG_2 and msg_M asynchronous messages are processed preferentially, while msG_3 synchronous messages are not. So when can these synchronous messages be processed? Remove the synchronization barrier by calling removeSyncBarrier().

Take a chestnut in your life. A concert, the audience need to line up at the gate of the stadium, in turn, theatre hall (normal sync of the audience is equivalent to the message queue queue messages), but this time the concert guests and the (equivalent to an asynchronous message, priority is higher than the audience), if they show their certificates (no papers, is equivalent to ordinary people, still need to line up, In this case, msg.setasynchronous (true) is only set, and a security guard will immediately block the audience from entering and allow the guest to enter first. Once all the staff are inside, if the security guards stop blocking the audience (i.e. remove the synchronization barrier), then the audience can enter (and process the synchronization message) again. As long as the security guard does not remove the intercept, the audience behind can never enter (the synchronous message will not be processed until the synchronization barrier is removed).

4. Use scenario of synchronization barrier

It seems that synchronization barriers are rarely used in everyday application development. So, what are the scenarios where synchronization barriers are used in the system source code? UI update messages in the Android system are asynchronous messages and need to be processed first.

For example, ViewRootImpl#scheduleTraversals() is called in many places, such as draw, requestLayout, invalidate, etc., when the View is updated:

//ViewRootImpl.java

    void scheduleTraversals() {
        if(! mTraversalScheduled) { mTraversalScheduled =true; MTraversalBarrier = mhandler.getLooper ().getQueue().postSyncbarrier (); / / send asynchronous messaging mChoreographer. PostCallback (Choreographer. CALLBACK_TRAVERSAL mTraversalRunnable, null);if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
Copy the code

PostCallback () eventually reached the ChoreographerpostCallbackDelayedInternal () :

Finally, ViewRootImpl#unscheduleTraversals() is called when the synchronization barrier is removed.

    void unscheduleTraversals() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false; // Remove the synchronization barrier mhandler.getLooper ().getQueue().removesyncBarrier (mTraversalBarrier); mChoreographer.removeCallbacks( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); }}Copy the code

5, summary

Synchronization barriers are set up to easily handle asynchronous messages with higher priority. When we call handler.getlooper ().getQueue().postsyncBarrier () and set the setAsynchronous(true) of the message, target is null and the synchronization barrier is enabled. When the message poller Looper loops through messages in loop(), if synchronization barriers are turned on, asynchronous messages are processed in preference to synchronous messages.