1: The application process starts to request drawing
In the last article, we saw that the UI has been added to the activity, but it hasn’t been shown yet. So when do you start showing it? The timing is after onResume. We look at the
The handleResumeActivity method of activityThread
@Override public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) { // If we are getting ready to gc after going to the background, Final ActivityClientRecord r = performResumeActivity(Token, finalStateRequest, Reason); if (r.window == null && ! a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); Decor set to INVISIBLE decor. SetVisibility (view.invisible); ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (r.mPreserveWindow) { a.mWindowAdded = true; r.mPreserveWindow = false; // Normally the ViewRoot sets up callbacks with the Activity // in addView->ViewRootImpl#setView. If we are instead reusing // the decor view we have to notify the view root that the // callbacks may have changed. ViewRootImpl impl = decor.getViewRootImpl(); Tell the view to redraw if (impl! = null) { impl.notifyChildRebuilt(); } } if (a.mVisibleFromClient) { if (! a.mWindowAdded) { a.mWindowAdded = true; Here you add the decorView to the WindowManager to begin drawing. wm.addView(decor, l); } else { // The activity will get a callback for this {@link LayoutParams} change // earlier. However, at that time the decor will not be set (this is set // in this method), so no action will be taken. This call ensures the // callback occurs with the decor set. a.onWindowAttributesChanged(l); // If the window has already been added, but during resume // We started another activity, Then don't yet make the // window visible.}... if (r.activity.mVisibleFromClient) { r.activity.makeVisible(); Set this to visual}}Copy the code
We look at the
@VisibleForTesting public ActivityClientRecord performResumeActivity(IBinder token, Boolean finalStateRequest,{call onResume method r.activity. PerformResume (R.tartsnotresumed, reason); }Copy the code
Start layout and draw UI when wm.addView(decor, L) is called
Let’s look at wm’s AddView method
Wm is called in the Attach method of the activity
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
Copy the code
Let’s see what (WindowManager)context.getSystemService(context.window_service) is? In contextImpl
@Override public Object getSystemService(String name) { return SystemServiceRegistry.getSystemService(this, name); } registerService(Context.WINDOW_SERVICE, WindowManager.class, new CachedServiceFetcher<WindowManager>() { @Override public WindowManager createService(ContextImpl ctx) { return new WindowManagerImpl(ctx); }}); kCopy the code
You can see that windowManage is Windows ManagerImpl and then look at the addView method of this class
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
Copy the code
Go to Windows ManagerGlobal next
public void addView(View view, ViewGroup.LayoutParams params, Display display, ViewRootImpl root = new viewRootImpl (view.getContext(), display); view.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); mParams.add(wparams); // do this last because it fires off messages to start doing things try { root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { // BadTokenException or InvalidDisplayException, clean up. if (index >= 0) { removeViewLocked(index, true); } throw e; }}}Copy the code
WindowManagerGlobal singletons exist in the current application process. Mview is the view that holds all Windows, mRoots holds rootImpl for all Windows, and mParams holds all view parameters.
2: ViewRootImpl stage
Root. setView(View, wparams, panelParentView);
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { requestLayout() } @Override public void requestLayout() { if (! MHandlingLayoutInLayoutRequest) {here to test whether the UI thread checkThread (); mLayoutRequested = true; scheduleTraversals(); } Here, add window to WMS, WMS manages the window, and then you can display it. try { mOrigWindowType = mWindowAttributes.type; mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mWinFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel); } catch (RemoteException e) { mAdded = false; mView = null; mAttachInfo.mRootView = null; mInputChannel = null; mFallbackEventHandler.setView(null); unscheduleTraversals(); setAccessibilityFocus(null, null); throw new RuntimeException("Adding window failed", e); } void scheduleTraversals() { if (! mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); Open synchronization barrier mChoreographer. PostCallback (send a subscription frame refresh callback. Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (! mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); }}Copy the code
In this method, the scheduleTraversals() method is called first and then addToDisplay is called, but PerformTraversals() in schedule is executed only after addToDisplay is executed.
3: Application window added
Let’s look at the mWindowSession addToDisplay.
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
Copy the code
The mWindowSession assignment is in
public ViewRootImpl(Context context, Display display) { mContext = context; mWindowSession = WindowManagerGlobal.getWindowSession(); } public static IWindowSession getWindowSession() { synchronized (WindowManagerGlobal.class) { if (sWindowSession == null) { try { InputMethodManager imm = InputMethodManager.getInstance(); IWindowManager windowManager = getWindowManagerService(); Here call binder remote invocation of the WMS OpenSession sWindowSession = windowManager. OpenSession (new IWindowSessionCallback. Stub () {@ Override public void onAnimatorScaleChanged(float scale) { ValueAnimator.setDurationScale(scale); } }, imm.getClient(), imm.getInputContext()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } return sWindowSession; } } @Override public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client, IInputContext inputContext) { if (client == null) throw new IllegalArgumentException("null client"); if (inputContext == null) throw new IllegalArgumentException("null inputContext"); Session session = new Session(this, callback, client, inputContext); return session; }Copy the code
Here shows mWindowSession addToDisplay for the Session. AddToDisplay, transfer data to the WMS through mWindowSession application process, and application to WMS process communication class for mWindow,
So let’s look at mWindow
public ViewRootImpl(Context context, Display display) {
mWindow = new W(this);
}
static class W extends IWindow.Stub {
}
Copy the code
In addToDisplay(mWindow…) Then the WMS gets the ability to send instructions to the application from mWindow, so let’s look at Session’s addToDisplay method
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel);
}
Copy the code
The window has been added to the WMS after this method is executed. :
4: Choreographer Stage
The next see scheduleTraversals method will be called mChoreographer postCallback
Return to the Choreographer
@TestApi 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); } private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) { if (DEBUG_FRAMES) { Log.d(TAG, "PostCallback: type=" + callbackType + ", action=" + action + ", token=" + token + ", delayMillis=" + delayMillis); } synchronized (mLock) { final long now = SystemClock.uptimeMillis(); final long dueTime = now + delayMillis; MCallbackQueues [callbackType]. AddCallbackLocked (dueTime, Action, token); If (dueTime <= now) {scheduleFrameLocked(now) if (dueTime <= now) {scheduleFrameLocked(now); } else { Message 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) { if (DEBUG_FRAMES) { Log.d(TAG, "Scheduling next frame on vsync."); } // If running on the Looper thread, then schedule the vsync immediately, // otherwise post a message to schedule the vsync from the UI thread // as soon as possible. if (isRunningOnLooperThreadLocked ()) {if running on the UI thread when this scheduleVsyncLocked (); } else { 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); if (DEBUG_FRAMES) { Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms."); } Message msg = mHandler.obtainMessage(MSG_DO_FRAME); msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, nextFrameTime); {}}} private void scheduleVsyncLocked () to the screen. Subscribe to refresh the callback mDisplayEventReceiver scheduleVsync (); } private final class FrameDisplayEventReceiver extends DisplayEventReceiver{ @Override public void run() { mHavePendingVsync = false; doFrame(mTimestampNanos, mFrame); }}Copy the code
The screen refresh frequency is mostly 60 frames, that is, every 16ms. Each time the screen refresh, the screen will send callbacks to subscribers. We have seen in the run method will perform doFrame FrameDisplayEventReceiver method. We look at the
void doFrame(long frameTimeNanos, int frame) { final long startNanos; synchronized (mLock) { if (! mFrameScheduled) { return; // no work to do } if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) { mDebugPrintNextFrameTimeDelta = false; Log.d(TAG, "Frame time delta: "+ ((frameTimeNanos -mlastFrametimenanos) * 0.00000f) +" ms"); } long intendedFrameTimeNanos = frameTimeNanos; startNanos = System.nanoTime(); final long jitterNanos = startNanos - frameTimeNanos; if (jitterNanos >= mFrameIntervalNanos) { final long skippedFrames = jitterNanos / mFrameIntervalNanos; if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) { Log.i(TAG, "Skipped " + skippedFrames + " frames! " + "The application may be doing too much work on its main thread."); } final long lastFrameOffset = jitterNanos % mFrameIntervalNanos; if (DEBUG_JANK) { Log.d(TAG, "Missed vsync by "+ (jitterNanos * 0.00000f) +" ms "+ "which is more than the frame interval of" + (mFrameIntervalNanos * 0.00000f); "+ "Skipping" + "skippedFrames" + "frame and setting frame" + "time to "+ (lastFrameOffset * 0.000001f) +" ms in the past."); } frameTimeNanos = startNanos - lastFrameOffset; } if (frameTimeNanos < mLastFrameTimeNanos) { if (DEBUG_JANK) { Log.d(TAG, "Frame time appears to be going backwards. May be due to a " + "previously skipped frame. Waiting for next vsync."); } scheduleVsyncLocked(); return; } if (mFPSDivisor > 1) { long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos; if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) { scheduleVsyncLocked(); return; } } mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos); mFrameScheduled = false; mLastFrameTimeNanos = frameTimeNanos; } here start try {trace.tracebegin (trace.trace_tag_view, "Choreographer#doFrame"); AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS); mFrameInfo.markInputHandlingStart(); Start with input actions doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos); mFrameInfo.markAnimationsStart(); Choreographer.CALLBACK_ANIMATION, frameTimeNanos); Processing drawing mFrameInfo. MarkPerformTraversalsStart (); doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos); Handle doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos) after the draw is complete; } finally { AnimationUtils.unlockAnimationClock(); Trace.traceEnd(Trace.TRACE_TAG_VIEW); } if (DEBUG_FRAMES) { final long endNanos = System.nanoTime(); Log.d(TAG, "Frame " + frame + ": Finished, took "+ (Endnanos-startnanos) * 0.00000f +" ms, Latency "+ (startNanos-frameTimenanos) * 0.00000f +" Ms. "); }} void doCallbacks(int callbackType, long frameTimeNanos) {iterate over the run of the call,.... try { Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]); for (CallbackRecord c = callbacks; c ! = null; c = c.next) { if (DEBUG_FRAMES) { Log.d(TAG, "RunCallback: type=" + callbackType + ", action=" + c.action + ", token=" + c.token + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime)); } c.run(frameTimeNanos); }}... } thisCopy the code
This method is just iterating through the callbacks, executing the run method of the call, before we go
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
Copy the code
MTraversalRunnable run look at this
final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } } void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); if (mProfile) { Debug.startMethodTracing("ViewAncestor"); } performTraversals(); if (mProfile) { Debug.stopMethodTracing(); mProfile = false; }}} private void performTraversals() {// Cache mView since it is used so much relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); . performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); Measure... performLayout(lp, mWidth, mHeight); Layout... performDraw(); To draw. }Copy the code
RelayoutWindow is called first in Perform
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, Boolean insetsPending throws RemoteException {... Int relayoutResult = mWindowSession.relayout(mWindow, mSeq, Params, (int) (mView.getMeasuredWidth() * appScale + 0.5f), (int) (mview.getMeasuredHeight () * appScale + 0.5f), viewlimit, insetsPending? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber, mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets, mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout, mPendingMergedConfiguration, mSurface); } inCopy the code
In windowSession
@Override
public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets,
Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
Surface outSurface) {
int res = mService.relayoutWindow(this, window, seq, attrs,
requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
outStableInsets, outsets, outBackdropFrame, cutout,
mergedConfiguration, outSurface);
return res;
}
Copy the code
It asks WMS to calculate the relevant window size, create a Surface on the WMS side, and so on; I don’t know how to do that anymore,
So the three perform methods,