Android advanced layout drawing process a setContentView source code interpretation

This article continues the above Android advanced (layout drawing process 1). SetContentVIew () is used to analyze PhoneWindow and Layoutinfate source code. This article will continue to learn and summarize, and briefly mention the classic interview questions. Andorid SDK: 28.0

The above analysis of The setContentView of Dao PhoneWindow has carried out a partial interpretation

@Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            // View rendering process 1 section 1
            installDecor();
        } else if(! hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); }if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            // View rendering process 1 section 4
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }

        // We continue our study from this method in turn. !!!!!!

        mContentParent.requestApplyInsets();

        final Callback cb = getCallback();
        if(cb ! =null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }
Copy the code

Here we go!

ViewRootImpl

/ / the view class

public void requestApplyInsets(a) {
    requestFitSystemWindows();
}

public void requestFitSystemWindows(a) {
    if(mParent ! =null) { mParent.requestFitSystemWindows(); }}Copy the code

MParent is the ViewRootImpl implementation of ViewParent, so the requestFitSystemWindows method in ViewRootImpl is called.


/ / ViewRootImpl class

@Override
    public void requestFitSystemWindows(a) {
        // Check whether the thread is in the main thread
        checkThread();
        mApplyInsetsRequested = true;
        // View drawing
        scheduleTraversals();
    }
Copy the code

ViewRootImpl.scheduleTraversals()


void scheduleTraversals(a) {
        // Enter only if no task is being drawn
        if(! mTraversalScheduled) { mTraversalScheduled =true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            // Send a message
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if(! mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); }... }}/ / Runnable implementation
final class TraversalRunnable implements Runnable {
        @Override
        public void run(a) {
            // Draw methoddoTraversal(); }}void doTraversal(a) {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }
            // The most important place to execute the view drawing process
            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false; }}}Copy the code

performTraversals()

private void performTraversals(a) {...// When loading the View for the first time, you need to adjust the window size, need to adapt to the system window, the View display state changes,
    // The view layout parameter is not empty, forcing the window to be rearranged. It is possible to perform the measurement
    if(mFirst || windowShouldResize || insetsChanged || viewVisibilityChanged || params ! =null || mForceNextWindowRelayout) {
       
        ...
        // The window does not stop or is notified that it needs to be drawn
        if(! mStopped || mReportNextDraw) { ...if(focusChangedDueToTouchMode || mWidth ! = host.getMeasuredWidth() || mHeight ! = host.getMeasuredHeight() || contentInsetsChanged || updatedConfiguration) { ...// step 1: Measure
                // Ask host how big it wants to beperformMeasure(childWidthMeasureSpec, childHeightMeasureSpec); . }}}else{... }...if (didLayout) {// Execute the layout
        // step 2: LayoutperformLayout(lp, mWidth, mHeight); . }...// If the drawing is not cancelled and the Surface is not new, perform the drawing
    if(! cancelDraw && ! newSurface) { ...// 3. Step 3: Draw
        performDraw();
    } else {// If the drawing is cancelled or the Surface is new, measure, lay out, and draw again. } mIsInTraversal =false;
}
Copy the code

The important method is in the above code through performMeasure(), performLayout (), performDraw() three methods, method also call View measure Layout and draw method respectively. This illustrates the three methods that we need to implement by default when writing custom views, and the order of the three methods.

View drawing relevant interview questions

Dispatching events

The previous article has been mentioned, here are two pictures, a picture is worth a thousand words touch event distribution flow chart reference article

Hardware to Android touch events

Dialog and Activity click events

Example: Clicking the Activity part of the Dialog button is not reflected when the Dialog is real. Why?

Let’s take a look at the Dialog initialization source

Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
        if (createContextThemeWrapper) {
            if (themeResId == ResourceId.ID_NULL) {
                final TypedValue outValue = new TypedValue();
                context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
                themeResId = outValue.resourceId;
            }
            mContext = new ContextThemeWrapper(context, themeResId);
        } else {
            mContext = context;
        }
        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        New PhoneWindow / /
        final Window w = new PhoneWindow(mContext);
        mWindow = w;
        // Inject the callback into the window
        w.setCallback(this);
        w.setOnWindowDismissedCallback(this);
        w.setOnWindowSwipeDismissedCallback(() -> {
            if(mCancelable) { cancel(); }}); w.setWindowManager(mWindowManager,null.null);
        w.setGravity(Gravity.CENTER);
        mListenersHandler = new ListenersHandler(this);
    }
Copy the code

After creating the phoneWindow, setContentView() sets the layout. Now we’re back to the source sequence of the previous article. With installDecor(), create a DecorView that takes the layout ID we passed in, Iteratively reading the view in XML Pull returns a View object and adds it to the DecorView.

public void show(a) {... mWindowManager.addView(mDecor, l); . }public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {.../ / create ViewRootImpl
            root = new ViewRootImpl(view.getContext(), display);
            view.setLayoutParams(wparams);
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
            try {
                // setView
                root.setView(view, wparams, panelParentView);
            } catch(RuntimeException e) { ... }}}Copy the code

Dialog passes the DecorView to the WindowManager implementation class via show(). It’s going to be called underneath the view wrootimPL’s View post input Stage. But since there are multiple ViewRootImpl, InputManagerService gets the most recent window based on the height of the Z-axis, Then execute the corresponding ViewRootImpl inside ViewPostImeInputStage surveillance method – > execute mView. DispatchPointerEvent (event). So the click event is consumed in the uppermost Dialog.

Draw caton source tracing

Because here the source hierarchy is more, not a two can be clear, recommend a big guy’s blog

The origin of app Caton 16ms Vsync