1. Overview
It is simply understood as if asynchronous messages are cut in queue and executed first.
Scene: Queue to buy tickets
- First, an ordinary user came to queue up and left after buying the ticket.
- Later, another VIP user A came to buy tickets and kept standing at the sales window and never left (ps: add a barrier)
- Immediately afterwards, another ordinary user C came, and then VIP user B came.
- VIP A told VIP B, don’t queue up and come to the window to buy tickets. VIP B left after buying the tickets. VIP A was called away by a buddy (removing the barrier)
- At this time, it was finally the turn of ordinary user C to buy tickets.
2. System application
Simply put, it is better than event callback execution, in order to do some operations with higher priority, such as view refresh.
When a Handler message comes, it takes precedence to perform synchronization barrier message events.
So that the system can do some message events that are more important than the upper-level business, so this method is annotated as hide, which is also the system that opens a backdoor for itself. Otherwise, if the method is exposed to the application for use, it is likely to cause the system to stutter and cause frame drops.
- Add a barrier before applying for VSYNC signal to ensure priority execution
- The handler here is the handler of the main thread
void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; //Set synchronization barriers to ensure that mTraversalRunnable is executed first mTraversalBarrier = ().getQueue().postSyncBarrier(); //Officially holds the Handler associated with the main looper, and then sends an asynchronous message to the main thread messageQueue through the Handler ( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); 。。。。 } }
The above instructions are very important to apply for VSYNC signal. If you do not apply for VSYNC in time, the screen will be unsmooth and stuttered. Therefore, Android is an important reason why time-consuming operations are not allowed on the main thread. Because a message is currently performing time-consuming operations in onHandlerMesage (main), then the VSYNC application will be in a waiting state, causing the screen to be unable to refresh within 16.6ms (generally speaking, the screen refresh frequency is 60Hz, that is, refresh once in 16.6ms)
After waiting for a VSYNC signal, callback the mTraversalRunnable task in callback
Only after being executed, remove the barrier and perform the View drawing process
void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; //Remove message barrier ().getQueue().removeSyncBarrier(mTraversalBarrier); if (mProfile) { ("ViewAncestor"); } performTraversals(); if (mProfile) { (); mProfile = false; } } }
3. Source code implementation
3.1 Message classification
Messages in Handler can be divided into two categories: synchronous messages and asynchronous messages. The message type can be found through the following functions
// public boolean isAsynchronous() { return (flags & FLAG_ASYNCHRONOUS) != 0; }
Generally speaking, there is no difference in the processing methods of these two messages, and the difference will only occur when a synchronization barrier is set.
3.2 Special handling of MessageQueue
3.2.1
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 = (); (); = when; msg.arg1 = token; Message prev = null; Message p = mMessages; if (when != 0) { while (p != null && <= when) { prev = p; p = ; } } if (prev != null) { // invariant: p == = p; = msg; } else { = p; mMessages = msg; } return token; } }
This function simply creates a Message object and adds it to the message list. At first glance, it seems nothing special, but there is a big difference here that the Message does not have a target, which means that it is not executed by the handler after being taken out by the looper.
Difference between empty messages created by postSyncBarrier and normal messages: No target:Handler
We usually send messages through Handler. The functions that send messages in Handler include post***, sendEmptyMessage, and sendMessage, and these functions will eventually call the enqueueMessage function: you can see that enqueueMessage sets the target field for msg
// private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { = this; //... return (msg, uptimeMillis); }
Then forward it to Handler in the looper for processing:
public static void loop() { final Looper me = myLooper(); final MessageQueue queue = ; for (;;) { //Fetch out the message, if there is no message, it will block Message msg = (); (msg); } }
Note that adding the message barrier does not call nativeWake(mPtr) to wake up the thread.
The enqueueMessage message is used to call nativeWake(mPtr) to wake up the thread. (ps: nativeWake will be triggered when the main thread is blocked)
It is easy to understand: the barrier is just for the subsequent asynchronous information. If there is no information, there is no need to wake up the thread. If there is information, it will naturally go to enqueueMessage to wake up.
3.2.2
Get the message in the message queue
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 = (); Message prevMsg = null; Message msg = mMessages; if (msg != null && == null) {//When encountering a synchronization barrier, target==null // Stalled by a barrier. Find the next asynchronous message in the queue. // do while loop through the message link table // When the loop is out, msg points to a "asynchronous message" closest to the table header, and if it does not, it will be null do { prevMsg = msg; msg = ; } while (msg != null && !()); } if (msg != null) { if (now < ) { //... } else { // Got a message. mBlocked = false; if (prevMsg != null) { //Remove msg from the message list = ; } else { mMessages = ; } = null; if (DEBUG) (TAG, "Returning message: " + msg); (); //Return asynchronous message return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } //... } //... } }
- When the synchronization barrier is set, the next function will ignore all synchronization messages and return asynchronous messages.
- In other words, if the first message is a barrier, then go back and see if there are any asynchronous messages.
- Yes: Let's see how long it will take before this message is triggered, and set a timeout to continue sleeping.
- No: just keep sleeping and wait for others to wake up. At this time, the barrier always exists in the head of the message queue.
In other words, after setting up the synchronization barrier SyncBarrier, the Handler will only process isAsynchronous asynchronous messages.
In other words, the synchronization barrier adds a simple priority mechanism to the Handler message mechanism, and the priority of asynchronous messages is higher than that of synchronous messages.
3.2.3 Remove the barrier
public void removeSyncBarrier(int token) { // Remove a sync barrier token from the queue. //...Omit...Remove the token message of the barrier in the queue //Wake up the thread if (needWake && !mQuitting) { nativeWake(mPtr); } } }
Removed a message barrier and did the following:
1. Remove the token message with sequence number
2. If the main thread is blocking, wake up the thread
3.3 Handler sends asynchronous information
How to send asynchronous messages
Usually when we use Handler to send messages, these messages are synchronous messages. If we want to send asynchronous messages, then when creating Handler, we use one of the following constructors (async passes true)
public Handler(boolean async); public Handler(Callback callback, boolean async); public Handler(Looper looper, Callback callback, boolean async); private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { = this; //target will not be null if (mAsynchronous) {// Default is false, and the message is marked as a synchronous (normal) message by default (true); } return (msg, uptimeMillis); }
Then all messages sent through the Handler will become asynchronous messages
4. Summary
- The general barrier is used in conjunction with asynchronous. The following ordinary messages will not be executed until the removeSyncBarrier is called;
- Message: ==null is marked as a barrier message.
- is setAsynchronous(true) as an asynchronous message;
- When we use handler to send messages, we judge whether to send asynchronous information based on the Handler's properties: Handler:postSyncBarrier and removeSyncBarrier methods are both @hide and cannot be called directly. They need to be used through reflection;
- postSyncBarrier will not wake up the thread, removeSyncBarrier will wake up the thread (when there is a message in the queue);
This is the article about the detailed explanation of the Android synchronization barrier mechanism sync barrier instance application. For more related content on Android sync barrier, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!