This is the fifth day of my participation in Gwen Challenge

ViewRootImpl

Android UI refresh represents an interface change, generally there are two methods can be called:

  • requestLayout
  • invalidate

Both methods are executed in the ViewRootImpl, which is the top level structure responsible for rendering the View tree, and both methods are invoked in scheduleTraversals, which translates to “perform traversals”.

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        }
    }
Copy the code

The scheduleTraversals method does three things:

  • Set the mTraversalScheduled argument to true. MTraversalScheduled is designed to prevent multiple calls to draw.
  • Add a synchronization barrier to the message queue. Here,mHandlerFor the main thread, the synchronization barrier is used to block synchronous messages in favor of asynchronous ones, so subsequent view-related messages must be asynchronous. After all, the View should be drawn first to prevent stalling.
  • Invoke Choreographer’s postCallback method.

Choreographer

Let’s start with the postCallback mTraversalRunnable object, which is inherited from Runnable, and the Run method, which calls the doTraversal method.

void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); performTraversals(); }}Copy the code

The doTraversal method also happens to do three things:

  • Set the mTraversalScheduled argument to true. Indicates that you are ready to accept the next drawing operation.
  • Remove synchronization barriers. Release sync messages
  • Perform the View tree drawing.performTraversalsMethod to measure, lay out, and draw the View tree structure from the root layout.

The postCallback method is used to execute the docallback method, but when?

First of all, we need to know the screen refresh frequency FPS, that is, the number of screen refresh times within 1s. At present, the refresh frequency of most mobile phones is between 60 and 120. If the refresh frequency is 60, the refresh frequency will be about every 16ms. Then you may have the following question:

  1. The relationship between the drawing of the View and the Vsync signal, when does the View draw?
  2. How to receive VSync signal, do I need to actively subscribe?

With these two issues we have led to an important class called Choreographer, which I translate as dancer. The description of this class in Android is to coordinate the timing of animation, input, and drawing, receive a vSYNC signal, and plan the next frame of action. The simple thing is that Choreographer registers a VSync signal receiver that accepts the arrival of the next frame and then performs the specified operations.

PostCallback method is executed to postCallbackDelayedInternal Choreographer.

private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) { synchronized (mLock) { final long now = SystemClock.uptimeMillis(); final long dueTime = now + delayMillis; MCallbackQueues [callbackType]. AddCallbackLocked (dueTime, Action, token); If (dueTime <= now) {scheduleFrameLocked(now); } else {MSG = mhandler. obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action); msg.arg1 = callbackType; msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, dueTime); }}}Copy the code
  • First add the callbackType, Action, and Token arguments passed in to CallbackQueues
  • Call by execution timescheduleFrameLockedThe scheduleFrameLocked method calls Handler to send a delayed asynchronous message before time runs out.
private void scheduleFrameLocked(long now) { if (! mFrameScheduled) { mFrameScheduled = true; If (USE_VSYNC) {/ / is the main thread (isRunningOnLooperThreadLocked ()) {/ / registered VSync receiver scheduleVsyncLocked (); } else {// Switch to the main thread and register the VSync receiver Message MSG = mhandler. obtainMessage(MSG_DO_SCHEDULE_VSYNC); msg.setAsynchronous(true); mHandler.sendMessageAtFrontOfQueue(msg); } } 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

USE_VSYNC defaults to true to receive VSync signals and then call scheduleVsyncLocked to register the receivers. Instead, the main thread Handler calls the doFrame method to perform the View operation.

Choreographer registers the VSync signal receiver with DisplayEventReceiver and when the next frame’s VSync signal is received, it will be called back to onVsync.

private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable { private boolean mHavePendingVsync; private long mTimestampNanos; private int mFrame; public FrameDisplayEventReceiver(Looper looper, int vsyncSource) { super(looper, vsyncSource); } @Override public void onVsync(long timestampNanos, int builtInDisplayId, int frame) { mTimestampNanos = timestampNanos; mFrame = frame; Message msg = Message.obtain(mHandler, this); msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS); } @Override public void run() { mHavePendingVsync = false; doFrame(mTimestampNanos, mFrame); }}Copy the code

DisplayEventReceiver implements the Runnable method, calls its own run method directly through the Handler in the onVsync method, and then executes the doFrame method

void doFrame(long frameTimeNanos, int frame) { final long startNanos; synchronized (mLock) { if (! mFrameScheduled) { return; // no work to do } ...... // The omitted code is used to perform the frame's time correction operation...... try { AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS); mFrameInfo.markInputHandlingStart(); doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos); mFrameInfo.markAnimationsStart(); doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos); mFrameInfo.markPerformTraversalsStart(); doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos); } finally { AnimationUtils.unlockAnimationClock(); Trace.traceEnd(Trace.TRACE_TAG_VIEW); }}Copy the code

Each of the doCallbacks is executed according to Type. Remember the Type passed by postCallback, which is Choreographer.CALLBACK_TRAVERSAL. Moving on to the actions in doCallbacks:

    void doCallbacks(int callbackType, long frameTimeNanos) {
        CallbackRecord callbacks;
        synchronized (mLock) {
            
            callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                    now / TimeUtils.NANOS_PER_MS);
            if (callbacks == null) {
                return;
            }
            
        for (CallbackRecord c = callbacks; c != null; c = c.next) {
              
                c.run(frameTimeNanos);
            }
    }
Copy the code

Get the corresponding CallbackRecord object according to the callbackType, and then execute the run method, which calls the action, the mTraversalRunnable run method that we originally passed.

Once the VSync signal is received, the ViewRootImpl’s doTraversal method is invoked to render the View tree. We can also answer the two questions raised above:

1. The relationship between View drawing and Vsync signal. When will the View be drawn?

The View is drawn by inserting a synchronization barrier into the message queue, registering the VSync receiver, and drawing immediately after receiving the VSync signal of the next frame, removing the synchronization barrier.

2. How to receive VSync signal? Is it necessary to actively subscribe?

VSync needs to register the signal receiver before it can be delivered. The Vsync signal is also synchronized according to the current time.

The role Choreographer plays in the entire View drawing process:

  1. Receives and performs view-related animation, input, and drawing.
  2. Registers the VSync receiver and performs the next frame operation, as well as synchronizing the time of the VSync signal.