View的绘制流程

    xiaoxiao2021-03-25  108

    上两篇文章介绍了Activity的启动流程和Activity的Window的创建过程。流程大体还能顺下来。但在Activity的Window的创建中,加载完成了DecorView,也在Activity的onResume方法后调用了Activity的makeVisible方法显示了我们的View,但是感觉中间了还是少了很多流程。那就是View的绘制,加载完成好像并不是绘制完成,View的绘制肯定是在View显示出来之前先完成,要不怎么来的显示。 所以我们回到ActivityThread的handleResumeActivity方法,

    final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) { ....... ActivityClientRecord r = performResumeActivity(token, clearHide); ....... 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(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (a.mVisibleFromClient) { a.mWindowAdded = true; wm.addView(decor, l); } // If the window has already been added, but during resume // we started another activity, then don't yet make the // window visible. } else if (!willBeVisible) { if (localLOGV) Slog.v( TAG, "Launch " + r + " mStartedActivity set"); r.hideForNow = true; } ....... r.activity.mVisibleFromServer = true; mNumVisibleActivities++; if (r.activity.mVisibleFromClient) { r.activity.makeVisible(); } ....... }

    这个handleResumeActivity方法里面我大体分了三部分,第一部分和第三部分之前都说过了,看中间的部分,wm.addView(decor, l);这里将view添加到了WindowManager中,WindowManager的实现类是WindowManagerImpl,它的addview方法如下

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

    调用了WindowManagerGlobal的addView方法,

    public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ........ 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. synchronized (mLock) { final int index = findViewLocked(view, false); if (index >= 0) { removeViewLocked(index, true); } } throw e; } ........ }

    见到了ViewRootImpl,并且调用了setView方法,而在setview方法里调用requestLayout方法,

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

    这里有一个checkThread方法,好像就是检查是不是在主线程更新UI,我们都知道是不能在子线程更新UI的,否则就会抛异常,应该就是从这来的,但是你有没有发现这个方法调用的时机?如果我在onCreate里面去new一个thread去更新UI会不会报错呢?onCreate方法执行在前,checkThread方法执行在后呢….如果在子线程里先让他sleep一秒然后再去更新UI呢?可以对比试验一下。 好了接着来看scheduleTraversals方法。

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

    这个方法里有一个mTraversalRunnable,它是一个Runnable任务,

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

    执行的是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方法。 View的绘制流程就是从ActivityThread的这个performTraversals方法开始的,它经过measure、layout和draw三个过程才能最终将一个View绘制出来。其中measure是测量View的宽和高,layout是确定View在父容器中位置,draw就是将View绘制在屏幕上。

    盗一张图来给大家看一下,performTraversals的工作流程:

    performTraversals会依次调用performMeasure、performLayout、performDraw方法。这三个方法完成顶级View的measure、layout、draw的三大流程。其中performMeasure方法会调用measure方法,measure方法里又会调用onMeasure方法。在onMeasure方法里会对所有的子元素进行measure过程。接着子元素会重复父容器的measure过程,如此反复就完成了整个View树的遍历。performLayout和performDraw的传递流程和performMeasure类似。

    捋一遍调用顺序: 【ActivityThread】(handleResumeActivity) 【WindowManager】(addView) 【WindowManagerImpl】(addView) 【WindowManagerGlobal】(addView) 【ViewRootImpl】(setView) 【ViewRootImpl】(requestLayout) 【ViewRootImpl】(scheduleTraversals) 【Choreographer】(postCallBack) 【TraversalRunnable】(run) 【ViewRootImpl】(doTraversal) 【ViewRootImpl】(performTraversals) 到performTraversals方法后面的流程就如上图所示的流程了。

    结束,欢迎留言纠错。

    转载请注明原文地址: https://ju.6miu.com/read-13585.html

    最新回复(0)