UI绘制流程二(从哪里开始绘制)

243 阅读2分钟
前言:在Activity的启动流程中我们分析到了ActivityThread类中的handleResumeActivity中的PerformResumeActivity方法市区实现Activity的onResume的生命周期

在该方法中我们接着往下看

@Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
            
           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.setVisibility(View.INVISIBLE);
            
            ViewManager wm = a.getWindowManager();
            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();
                if (impl != null) {
                    impl.notifyChildRebuilt();
                }
            }
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    
                    a.mWindowAdded = true;
                    //把DecorView的ViewRootImpl传进去
                    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);
                }
            }
            
    }        

可以看到通过ViewManager wm = a.getWindowManager();a就是Activity,getWindowManager创建出WindowManager,我们进入该类,发现是个接口,老规矩我们去寻找实现类,直接查找WindowManagerImpl,找到addView方法

@Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

发现调用了mGlobal.addView,mGlobal是WindowManagerGlobal,跟进去

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
            ...
            ViewRootImpl root;
            View panelParentView = null;
            
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }
            ...
            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;
            }
                       
}            

看到mViews,mRoots,mParams是三个集合,分别添加进DecorView,DecorView的ViewRootImpl,以及DecorView的layoutParams,通过panelParentView = mViews.get(i)可以看到panelParentView其实就是DecorView本身,然后下面执行root.setView(view, wparams, panelParentView),跟进去

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    ...
    //寻找关键代码,找啊找
    ...
    // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                requestLayout();
}

突然发现熟悉的方法requestLayout(),就是我们调用该方法去重新刷新布局,跟进去

@Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

checkThread()方法就是检查当前线程是不是主线程,然后我们看到scheduleTraversals()方法在ActivityThread中有该方法,跟进去

void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

关键代码mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);发现传进来一个mTraversalRunnable,点击发现是一个TraversalRunnable类,并且实现了Runnable

final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

我们看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;
            }
        }
    }

发现调用了performTraversals,继续跟进,然后我们看到一些跟布局绘制有关的信息WindowManager.LayoutParamsRect,我们继续往下找返现找到了我们熟悉的

private void performTraversals() {
    ...
    // Ask host how big it wants to be
   performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
   ...
   performLayout(lp, mWidth, mHeight);
   ...
   performDraw();
}

总结下流程

View绘制总结就是
1、绘制入口 ActivityThread.handleResumeActivity -->WindowManagerImpl.addView(dercorView,layoutParams)-->WindowManagerGlobal.addView()
2、绘制的类及方法 ViewRootImpl(decorView,layoutParams,parentView)-->ViewRootImpl.requestLayout()-->scheduleTraversals()-->doTraversal()-->performTraversals()
3、绘制三大步骤 测量:ViewRootImpl.performMeasure 布局:ViewRootImpl.performLayout 绘制:ViewRootImpl.performDraw