1. The background

A fan recently asked me a very insightful question.

Will postSyncBarrier be created after the view.requestLayout () method is called?

At first glance, it seems a little out of line. If you think about it, I’ve split this problem into two questions that I’ll focus on in this article to explain the story behind requestLayout.

If view.requestLayout () is called after the screen is locked, will requestLayout() be called up layers?

After the screen is locked, the view.requestLayout () call will trigger the View measurement and layout operation?

PostSyncBarrier = postSyncBarrier = postSyncBarrier = postSyncBarrier = postSyncBarrier = postSyncBarrier = postSyncBarrier = postSyncBarrier = postSyncBarrier = postSyncBarrier So I imagined a scene.

Suppose view.requestLayout () is called every second in the Activity onResume(), but not stopped in the onStop() method. When the user locks the screen or presses the Home button.

The scene I imagined, in the words of Luo Xiang, is “permitted by law, but not advocated”. When the Activity is not in at the front desk, you should put requestLayout () method is stopped, we know, this method will call from the View layer calls up until ViewRootImpl requestLayout () method, It then triggers the View’s measurement and layout and even drawing methods from the top down. What a waste! The mistake is very elementary! But is it?

There is a popular Internet saying: You think I am on the first floor, but actually I am on the tenth floor. I’ll use a hierarchy to indicate how well I know the requestLayout method. The higher the hierarchy, the deeper the understanding.

As fans who know me know, I like to use tree diagrams to analyze the Android View source code. Above:

2. The first layer (up, layer by layer)

Which View requestLayout methods are triggered when i.requestlayout () is called?

A: I.equestlayout () -> C. equestLayout() -> A.equestlayout () ->… Omit some View – > ViewRootImpl. RequestLayout ()

//View.java public void requestLayout() { // 1. Clear the measurement record if (mMeasureCache! = null) mMeasureCache.clear(); / / 2. Increase PFLAG_FORCE_LAYOUT give mPrivateFlags mPrivateFlags | = PFLAG_FORCE_LAYOUT; mPrivateFlags |= PFLAG_INVALIDATED; // 3. If mParent has not called requestLayout, call it. In other words, if it has been called, it will not continue to call if (mParent! = null && ! mParent.isLayoutRequested()) { mParent.requestLayout(); }}Copy the code

The functions of this method are as follows:

  1. Clear measurement record
  2. Add PFLAG_FORCE_LAYOUT to mPrivateFlags
  3. RequestLayout is called if mParent has not already called it. In other words, if it was called, it will not be called again

Focus on the mParent. IsLayoutRequested () method, it is in the View. The specific implementation in Java

//View.java
 public boolean isLayoutRequested() {
    return (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
}
Copy the code

If mPrivateFlags increases the PFLAG_FORCE_LAYOUT flag bit, the View is considered to have requested the layout. From the previous paragraph, this flag bit is added in the second step of the requestLayout. Those familiar with bit operations will know that there will be an increase operation will have a corresponding clear operation. After some searching, we found:

//View.java public void layout(int l, int t, int r, int b) { // ... MPrivateFlags &= ~PFLAG_FORCE_LAYOUT; mPrivateFlags &= ~PFLAG_FORCE_LAYOUT; mPrivateFlags3 |= PFLAG3_IS_LAID_OUT; / /... Omit code}Copy the code

The PFLAG_FORCE_LAYOUT flag is cleared after the View method is called. The next time the View calls the requestLayout method, it can still call one layer up. But if the Layout () method does not execute, the next time the requestLayout method is called, it will not be called up the hierarchy.

So answer the first question at the beginning of the article:

If view.requestLayout () is called after the screen is locked, will requestLayout() be called up layers?

A: After the lock screen, except for the first call will be called up layer, other will not

Why, only the first call? PFLAG_FORCE_LAYOUT cannot be cleared because the layout method was not executed. If you want to know what happened, keep reading

If you know that a requestLayout call is a hierarchical call, congratulations, you are already at the first level of awareness. Here’s a second floor ticket for you.

3. The second layer (ViewRootImpl requestLayout)

Let’s take a look at the first layer as ViewRootImpl. RequestLayout ()

//ViewRootImpl.java @Override public void requestLayout() { if (! mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); } } void scheduleTraversals() { if (! mTraversalScheduled) { mTraversalScheduled = true; MTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); / / 2. To save mTraversalRunnable into Choreographer mChoreographer. PostCallback (Choreographer CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (! mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); }}Copy the code

The main functions of this method are as follows:

  1. Send a synchronization barrier message to the MessageQueue corresponding to the main thread Handler
  2. Save mTraversalRunnable into Choreographer

There are three particularly important points here:

  1. mTraversalRunnable
  2. MessageQueue synchronization barrier
  3. Choreographer mechanism

MTraversalRunnable is relatively simple and performs performMeasure, performLayout, performDraw from view otimPL from top to bottom. [key: This is executed when the Vsync signal is sent to the MessageQueue corresponding to the main thread Handler. Since scheduleTraversals() sent a synchronization barrier message to the MessageQueue, Then when a synchronous barrier message is executed, the asynchronous message is taken out for execution

4. Third level TraversalRunnable

When the Vsync semaphore arrives, Choreographer sends an asynchronous message. When asynchronous messaging execution, will call ViewRootImpl. MTraversalRunnable callback.

final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); }}Copy the code
void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); if (mProfile) { Debug.startMethodTracing("ViewAncestor"); } performTraversals(); if (mProfile) { Debug.stopMethodTracing(); mProfile = false; }}}Copy the code

What it does:

  1. Remove synchronization barrier
  2. Execute performTraversals method

The performTraversals() method is particularly complex and gives the pseudocode below

private void performTraversals() { if (! mStopped || mReportNextDraw) { performMeasure() } final boolean didLayout = layoutRequested && (! mStopped || mReportNextDraw); if (didLayout) { performLayout(lp, mWidth, mHeight); } boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || ! isViewVisible; if (! cancelDraw && ! newSurface) { performDraw(); }}Copy the code

Functions of this method:

  1. Invoke performMeasure() if conditions are met
  2. Call performLayout() if the conditions are met
  3. Call performDraw() if the condition is met

MStopped indicates whether the Activity is in the stopped state. If the Activity calls the onStop method, the performLayout method is not called.

//ViewRootImpl.java private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight) { // ... Omit codes host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); / /... Omit code}Copy the code

Answer the second question at the beginning of the passage:

After the screen is locked, the view.requestLayout () call will trigger the View measurement and layout operation?

A: No, because the Activity is currently in the stopped state

Since the view.layout () method will not be executed, PFLAG_FORCE_LAYOUT will not be cleared and subsequent requestLayout methods will not be called.

So far both questions of this article have been answered.

When I submitted my question to The Hon Yang boss’s WanAndroid, the boss sent me another question.

If the Activity onStop method causes the requestLayout method to fail to execute, the onResume method will not execute the previous requestLayout method.

So I wrote a demo to verify that

//MyDemoActivity.kt
override fun onStop() {
    super.onStop()
    root.postDelayed(object : Runnable {
        override fun run() {
            root.requestLayout()
            println("ChoreographerActivity  reqeustLayout")
        }
    }, 1000)
}
Copy the code

Print the log in the onLayout method of the custom layout

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    System.out.println("ChoreographerActivity onLayout");
    super.onLayout(changed, left, top, right, bottom);
}
Copy the code

After 1 second, requestLayout is called. The log is not printed. After 1 second, the screen is on, and the log is printed.

so

If the Activity onStop method causes the requestLayout method to fail to execute, the onResume method will not execute the previous requestLayout method.

Me: After demo verification. Let me tell you why

With demo, it’s easy to figure out why. The front is hard to break, so let’s debug it. But where is the break point? Thought about it. I think a breakpoint in the send synchronization barrier is better, ViewRootImpl. ScheduleTraversals (). Why is the breakpoint there? (Then you need to understand synchronization barriers and vsync refresh mechanisms, which will be covered later.)

When the screen lights up, the breakpoint is executed. You can see from the stack that the Activity’s performRestart() method executes the scheduleTraversals method of ViewRootImpl.RequestLayout is not executed when the screen is on, but PFLAG_FORCE_LAYOUT is still present because the view. requestLayout method is executed for 1s after the screen is locked. When the performTraversals method is invoked on the screen, measures, Layout, and Draw are performed.

So far, the perfect answer to the fans and hong Yang big guy’s question

5. Layer 4 (Handler synchronization barrier)

The Handler principle is also a must-ask interview question. There’s a lot of knowledge involved. Threads, Looper, MessageQueue, ThreadLocal, linked lists, underlying technologies, etc. I won’t go into this article. If you don’t know much about Handler. It does not affect the level of learning. However, it is highly recommended that you finish reading this article before taking a make-up lesson.

Student A: Synchronization barrier. Feel so high and mighty? Can you tell me about it?

Me: At first glance, it is quite lofty. It’s daunting. But if you think about it, it’s not that hard, basically, to divide messages into three different types

A: What do you mean? I’d like to hear more about it

Me: The following code should understand?

class Message{ int mType; Public static final int SYNC_BARRIER = 0; Public static final int NORMAL = 1; Public static final int ASYNCHRONOUS = 2; }Copy the code

A: This is very simple. We usually use different values to indicate different types in development, but the Android Message class does not have these different values.

Me: The Android Message class really doesn’t use different values to represent different types of messages. It is composed of three different types of Message via target and isAsynchronous().

Message type target isAsynchronous()
Synchronous barrier messages null It doesn’t matter
Asynchronous messaging Not null Returns true
Ordinary message Not null Returns false
A: I see. What are the differences?

Me: There are only normal messages in the world, but because of priorities, there are synchronous barrier messages and asynchronous messages. They are used together. When all three messages are in the message queue, asynchronous messages are preferentially executed if a synchronous barrier message is encountered.

Student A: A little dizzy

Me: Don’t worry, just look at the diagram below

  1. Green for ordinary messages, very disciplined, in order to join the queue out.
  2. Red indicates an asynchronous message, which means it is in a hurry and has priority.
  3. Yellow indicates synchronous barrier messages, which serve as a warning and then only send asynchronous messages out of the queue. If there is no asynchronous message, it will wait.

As shown in the figure above, the message queue is full of normal messages. So they’re going to come out of the queue, in order, at the head of the queue. msg1->msg2->msg3

As shown above, all three types of messages exist, msg1 being a synchronization barrier message. The synchronous barrier message does not actually execute, and it does not automatically exit the queue, requiring a call to MessageQueue’s removeSyncBarrier() method. It acts as an “alert” and then prioritizes the red messages out of the queue.

  1. Msg3 out queue

2. Msg5 is out of the queue

  1. At this point, MSG2 does not exit the queue, there are no red messages in the queue, but there are yellow messages, so it will wait for the red message, and the green message will not be executed

  1. Call the removeSyncBarrier() method to dequeue Msg1

  1. Green messages line up in order

PostSyncBarrier () and removeSyncBarrier() must occur in pairs, otherwise the message queue will suffer from false death.

So much for the synchronization barrier. If you don’t understand it, you are advised to search for other materials online.

6. Level 5 (Choreographer Vsync)

Student B: Does vsync feel so elegant? Can you tell me about it

Me: this thing compare bottom layer, I also too clear, but have a more clever way of understanding.

B: Tell me about it.

Me: Observer mode. You know, vsync signals are emitted from the bottom. I don’t know exactly, but there is a class on top that listens for signals from Vsync and when it receives signals, Choreographer sends asynchronous messages to message queues. One of the functions of this message is to tell ViewRootImpl to perform measurement, layout, and draw operations.

//Choreographer.java private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable { private boolean mHavePendingVsync; private long mTimestampNanos; private int mFrame; @Override public void onVsync(long timestampNanos, int builtInDisplayId, int frame) { //... Long now = system.nanotime (); If (timestampNanos > now) {log. w(TAG, "Frame time is "+ ((timestampNanos - now) * 0.00000f) +" ms in the future! Check that graphics HAL is generating vsync " + "timestamps using the correct timebase."); 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); msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS); }Copy the code

7. Layer 6 (Drawing Mechanism)

ViewRootImpl and Choreographer are the two main protagonists of the drawing mechanism. They are responsible for the following functions. I’m not going to expand on that.

8. How to ask Hongyang God

If you have something to add to this article, please leave a message. If you think this article is good, don’t be afraid to share it with your friends. Last but not least, give you a bonus. Follow me and reply “Watch” to get my chat record with Hongyang God.

9. Great articles from the past

  • [dry | explanation bitmap algorithm application in Android RecyclerView

] (mp.weixin.qq.com/s/xi7qUqGr5…).

  • Deep understanding of RecyclerView layout and animation principle
  • RecyclerView Rolling recycling and reuse mechanism
  • Talk about RecyclerView in detail
  • One of the four steps for event distribution: In-depth traversal Explains the Android event distribution mechanism
  • Event distribution tetralogy 2: Nested sliding event analysis
  • Event distribution: CoordinatorLayout event analysis
  • Event distribution four tetralogy: understand BottomSheetBehavior