NativePollOnce () and nativeWake() are used to wait and wake up, and MSG. When is used to delay messages. 1 What is AsyncMessage? 2 Why should Looper be placed in ThreadLocal? 3 Where is activityThread.main () called

1 Asynchronous message AsyncMessage

Let’s look at where asynchronous messages are encountered in the first article:

Message next() { ... Omit some code... for (; ;) {... Omit some code... synchronized (this) { Message prevMsg = null; Message msg = mMessages; MSG. IsAsynchronous if (MSG! = null && msg.target == null) { do { prevMsg = msg; msg = msg.next; } while (msg ! = null && ! msg.isAsynchronous()); }... Omit some code... }}}Copy the code

Let’s look at the MSG. IsAsynchronous code:

public boolean isAsynchronous() { return (flags & FLAG_ASYNCHRONOUS) ! = 0; } static final int FLAG_ASYNCHRONOUS = 1 << 1;Copy the code

So let’s see where we add this flag:

public void setAsynchronous(boolean async) { if (async) { flags |= FLAG_ASYNCHRONOUS; } else { flags &= ~FLAG_ASYNCHRONOUS; }}Copy the code

The message.setasynchronous (Boolean async) method is used to control whether or not a Message is asynchronous. Where is it called?

public void requestLayout() { ... Omit some code... // mParent is the parent View, if the View is already the top View, mParent is ViewRootImpl if (mParent! = null && ! mParent.isLayoutRequested()) { mParent.requestLayout(); }... Omit some code... }Copy the code

There are layer upon layer upward call mParent. RequestLayout (), until the call to ViewRootImpl. RequestLayout ()

@Override public void requestLayout() { if (! mHandlingLayoutInLayoutRequest) { checkThread(); // Check the current thread mLayoutRequested = true; // Update scheduleTraversals(); // start traversing}}Copy the code
Void checkThread() {if (mThread! = Thread.currentThread()) { throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views."); }}Copy the code
public boolean mTraversalScheduled; int mTraversalBarrier; Choreographer mChoreographer; void scheduleTraversals() { if (! MTraversalScheduled) {// mTraversalScheduled = true; // Add a synchronization barrier to mHandler's MessageQueue. A synchronization barrier is also a Message, but the target of this Message is null. It can also have an AsynchronousMessage AsynchronousMessage mTraversalBarrier = mhandler.getlooper ().getqueue ().postsyncbarrier (); // This is important!! , Choreographer can post a message, //TAG1: CALLBACK_TRAVERSAL, remember this TAG mChoreographer. PostCallback (Choreographer. CALLBACK_TRAVERSAL mTraversalRunnable, null); . Omit some code... }}Copy the code

We take the MessageQueue. PostSyncBarrier () :

Return postSyncBarrier(systemclock. uptimeMillis()); return postSyncBarrier(systemclock. uptimeMillis()); } private int postSyncBarrier(long when) { synchronized (this) { final int token = mNextBarrierToken++; //token increment 1 final Message MSG = message.obtain (); msg.markInUse(); msg.when = when; MSG. Arg1 = token; //arg1 is token // note!! Message prev =null; Message prev =null; Message p = mMessages; If (when! = 0) { while (p ! = null && p.when <= when) { prev = p; p = p.next; }} // Insert into queue if (prev! = null) { msg.next = p; prev.next = msg; } else { msg.next = p; mMessages = msg; } return token; }}Copy the code

RemoveSyncBarrier ():

Public void removeSyncBarrier(int token) {synchronized (this) null; Message p = mMessages; / / as long as p.t arget! =null || p.arg1! Arg1 =token while (p! = null && (p.target ! = null || p.arg1 ! = token)) { prev = p; p = p.next; If (p == null) {throw new IllegalStateException("The Specified message queue synchronization "+" barrier token has not been posted or has already been removed."); } final boolean needWake; // Remove synchronization barrier message if (prev! = null) { prev.next = p.next; needWake = false; } else { mMessages = p.next; needWake = mMessages == null || mMessages.target ! = null; } // Discard p.ricycleunchecked (); // If needWake is needed, wake waits if (needWake &&! mQuitting) { nativeWake(mPtr); }}}Copy the code

Then watch mChoreographer. PostCallback (Choreographer. CALLBACK_TRAVERSAL mTraversalRunnable, null); The first argument is an int, and the second argument, mTraversalRunnable, is a Runnable:

final TraversalRunnable mTraversalRunnable = new TraversalRunnable(); final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); }} void doTraversal() {if (mTraversalScheduled) {// mTraversalScheduled = false; // Remove the synchronization barrier from mHandler's MesssageQueue, Mhandler.getlooper ().getQueue().removesyncBarrier (mTraversalBarrier); // Start traversals of layout performTraversals(); }} // This method measures, layouts and traversals the view, which we'll talk about in detail later: private void performTraversals() {...... performMeasure(); . performLayout(); . performDraw(); . }Copy the code

Let’s take a look at some of the key code for the Choreographer class:

class Choreographer { public static final int CALLBACK_INPUT = 0; public static final int CALLBACK_ANIMATION = 1; public static final int CALLBACK_INSETS_ANIMATION = 2; public static final int CALLBACK_TRAVERSAL = 3; Public static final int CALLBACK_COMMIT = 4; private static final int CALLBACK_LAST = CALLBACK_COMMIT; private final CallbackQueue[] mCallbackQueues; private final FrameHandler mHandler; private final FrameDisplayEventReceiver mDisplayEventReceiver; private Choreographer(Looper looper, int vsyncSource) { mLooper = looper; mHandler = new FrameHandler(looper); MDisplayEventReceiver = USE_VSYNC? new FrameDisplayEventReceiver(looper, vsyncSource) : null; mLastFrameTimeNanos = Long.MIN_VALUE; mFrameIntervalNanos = (long)(1000000000 / getRefreshRate()); Queues are initialized with the mCallbackQueues () function. Queues are initialized with the mCallbackQueues () function. CALLBACK_LAST=4 mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1]; for (int i = 0; i <= CALLBACK_LAST; i++) { mCallbackQueues[i] = new CallbackQueue(); Public void postCallback(int callbackType, Runnable action, Object token) { postCallbackDelayed(callbackType, action, token, 0); } public void postCallbackDelayed(int callbackType,Runnable action, Object token, long delayMillis) { if (action == null) { throw new IllegalArgumentException("action must not be null"); } if (callbackType < 0 || callbackType > CALLBACK_LAST) { throw new IllegalArgumentException("callbackType is invalid");  } postCallbackDelayedInternal(callbackType, action, token, delayMillis); } / / here eventually transferred to the private void postCallbackDelayedInternal (int callbackType, Object action, Object token, long delayMillis) { synchronized (mLock) { final long now = SystemClock.uptimeMillis(); Final Long dueTime = now + delayMillis; // This is an array callbackType is CALLBACK_TRAVERSAL, dueTime=0, The action is the mTraversalRunnable and the mTraversalRunnable is stored in the collection corresponding to the CALLBACK_TRAVERSAL mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token); if (dueTime <= now) { //true scheduleFrameLocked(now); MSG = mhandler. obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action); msg.arg1 = callbackType; msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, dueTime); }} private void scheduleFrameLocked(long now) {if (! mFrameScheduled) { mFrameScheduled = true; If (USE_VSYNC) {/ / see if here (isRunningOnLooperThreadLocked ()) {scheduleVsyncLocked (); } else { Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); //msg.what = MSG_DO_SCHEDULE_VSYNC msg.setAsynchronous(true); // mark as an asynchronous message!! We added a synchronization barrier in front of the mHandler. SendMessageAtFrontOfQueue (MSG); / / to send, } else {final Long nextFrameTime = math.max (mLastFrameTimeNanos/timeUtils.nanOS_per_ms + sFrameDelay, now); Message msg = mHandler.obtainMessage(MSG_DO_FRAME); msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, nextFrameTime); }}}}Copy the code

Now look at the code for FrameHandler:

private final class FrameHandler extends Handler { public FrameHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_DO_FRAME: doFrame(System.nanoTime(), 0); break; Case MSG_DO_SCHEDULE_VSYNC: // doScheduleVsync(); break; case MSG_DO_SCHEDULE_CALLBACK: doScheduleCallback(msg.arg1); break; }}}Copy the code

Then look at

void doScheduleVsync() { synchronized (mLock) { if (mFrameScheduled) { scheduleVsyncLocked(); } } } private void scheduleVsyncLocked() { mDisplayEventReceiver.scheduleVsync(); } // check the scheduleVsync method: public void scheduleVsync() { if (mReceiverPtr == 0) { Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event " + "receiver has already been disposed."); } else {return nativeScheduleVsync(mReceiverPtr); }}Copy the code

Native finished after the callback to FrameDisplayEventReceiver. OnVsync () :

private final class FrameDisplayEventReceiver extends DisplayEventReceiverimplements Runnable { private boolean mHavePendingVsync; private long mTimestampNanos; private int mFrame; public FrameDisplayEventReceiver(Looper looper, int vsyncSource) { super(looper, vsyncSource, CONFIG_CHANGED_EVENT_SUPPRESS); } @Override public void onVsync(long timestampNanos, long physicalDisplayId, int frame) { long now = System.nanoTime(); if (timestampNanos > now) { timestampNanos = now; } if (mHavePendingVsync) { Log.w(TAG, "Already have a pending vsync event. There should only be " + "one at a time."); } else { mHavePendingVsync = true; } mTimestampNanos = timestampNanos; mFrame = frame; Message msg = Message.obtain(mHandler, this); // The second argument here is this, which means msg.callback = this, and will run to its own run function msg.setasynchronous (true); mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS); } @override public void run() {mHavePendingVsync = false; doFrame(mTimestampNanos, mFrame); }}Copy the code
void doFrame(long frameTimeNanos, int frame) { final long startNanos; synchronized (mLock) { if (! mFrameScheduled) { return; // no work to do } ... Omitted code for calculating time... 1 Input event 2 Animation 3 layout try {trace.tracebegin (trace.trace_tag_view, "Choreographer#doFrame"); AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS); mFrameInfo.markInputHandlingStart(); doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos); mFrameInfo.markAnimationsStart(); doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos); mFrameInfo.markPerformTraversalsStart(); //TAG: CALLBACK_TRAVERSAL (Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos) doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos); } finally { AnimationUtils.unlockAnimationClock(); Trace.traceEnd(Trace.TRACE_TAG_VIEW); } } void doCallbacks(int callbackType, long frameTimeNanos) { CallbackRecord callbacks; Synchronized (mLock) {synchronized (mLock) { The type: CALLBACK_TRAVERSAL callbacks = mCallbackQueues [callbackType] extractDueCallbacksLocked (now / TimeUtils.NANOS_PER_MS); if (callbacks == null) { return; } mCallbacksRunning = true; . Omit some code... } try {// iterate over all events, execute, Remember our previous that: mChoreographer postCallback (Choreographer. CALLBACK_TRAVERSAL mTraversalRunnable, null)? // The second argument is I and a traversal will be executed, that is, into our traversal(), which will measure, map, and draw for (CallbackRecord c = callbacks; c ! = null; c = c.next) { c.run(frameTimeNanos); } } finally { ... Recycling callback... }}Copy the code

Through the above analysis, we know: Android uses asynchronous messaging in its layout. Why? What are the benefits? Asynchronous messages are preferentially processed, for example, if there are 100 messages in the current MessageQueue, then a layout message comes in. Normally, it has to wait for 100 messages to finish. If it is asynchronous, it can be preferentially executed, so that the UI does not get stuck because of too many messages. Let’s look at messagequeue.next:

Message next() { ... Omit some code... for (;;) {... Omit some code... synchronized (this) { final long now = SystemClock.uptimeMillis(); PrevMsg = null; Message msg = mMessages; // target == null! As mentioned above, this is a synchronous barrier, and there is usually an asynchronous message behind a synchronous barrier. The purpose of the synchronous barrier is to mark if (MSG! = null &&msg. target == null) {// If there is a synchronization barrier, then look for asynchronous message do {prevMsg = MSG; msg = msg.next; } while (msg ! = null && ! msg.isAsynchronous()); If (MSG!) {if (MSG!) {if (MSG! = null) { ... Deal with MSG... NextPollTimeoutMillis = -1;} else {// mark nextPollTimeoutMillis = -1; }... Omit some code... }... Omit code for handling idle messages... }}Copy the code

The above code is omitted, mainly analyzing the asynchronous messages are preferentially executed, and the asynchronous messages are behind the synchronization barrier

2 Why put Looper in ThreadLocal

How to put a Looper in a ThreadLocal? Looper.prepare()

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() ! = null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }Copy the code

ThreadLocal.set()

public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map ! = null) map.set(this, value); else createMap(t, value); }Copy the code

Threadlocalmap.getmap () you can see that Thread has a variable ThreadLocalMap

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}
public class Thread implements Runnable {
    ThreadLocal.ThreadLocalMap threadLocals = null;

}
Copy the code

CreateMap () creates a ThreadLocalMap directly

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}
Copy the code

To summarize the Looper logic, each thread will have a member of ThreadLocalMap called threadLocals, which can be interpreted as a HashMap, where the key is ThreadLocal, Value is of any type. 2. Looper Contains a ThreadLocal member variable sThreadLocal. SThreadLocal = sThreadLocal; looper = sThreadLocal; looper = sThreadLocal;

Looper looler = new Looper(); // Create a looper ThreadLocalMap map = thread.currentThread ().getMap(); Map. set(sThreadLocal, looper); // Store looper into the ThreadLocalMap of the current threadCopy the code

How to get Looper from ThreadLocal?

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
Copy the code

ThreadLocal gets (); get(); Since ThreadLocal is A member variable of each thread, using looper.mylooper () in thread A is the Looper of thread A, and the Looper of thread B is the Looper of thread B. The UI thread must be the Looper of the UI thread. So that’s what thread switching is all about.

3 Where is activityThread.main () called

A: This is called when the app process is being created. When the app process is being created, zygote is used to fork a new process as the app process, and then reflection is used to call the main method of ActivityThread

conclusion

Target =null (MSG, target=null, MSG, target=null); Asynchronous messages are characterized by isAsynchronous = true. Eg: If the synchronous barrier is 50 and the asynchronous message is 100, the 1-49 in front of the synchronous barrier will be executed first, and when it reaches 50, it will be found to be a synchronous barrier. Then, it will traverse the subsequent messages, find the asynchronous message 100, remove the synchronous barrier 50, and continue to execute the following message 51, and so on

2 ThreadLocal is used to store Looper inside the thread for cross-thread purposes