Activity布局繪制流程源碼解析

Activity布局加載流程源碼解析一文中,我們分析了Activity布局加載流程,通過(guò)分析我們了解到Activity通過(guò)Window來(lái)控制界面的展示,一個(gè)Activity包含一個(gè)Window對(duì)象(具體由PhoneWindow來(lái)實(shí)現(xiàn)),并且PhoneWindow將DecorView作為整個(gè)應(yīng)用窗口的根View。當(dāng)時(shí),我們?cè)诜治龅臅r(shí)候遺留了一個(gè)問(wèn)題,那就是DecorView是什么時(shí)候添加到Window上去的?那么,這篇文章就來(lái)具體介紹一下Activity布局繪制流程的源碼分析,而且源碼版本基于A(yíng)ndroid 8.0。

一、從ActivityThread的handleResumeActivity方法說(shuō)起

在A(yíng)ctivity的onCreate()方法中,我們通過(guò)setContentView()方法完成了xml布局解析以及DecorView的創(chuàng)建。之后,DecorView會(huì)被添加到Window上去,這個(gè)過(guò)程的起始點(diǎn)在A(yíng)ctivityThread的handleResumeActivity方法中。

    final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ActivityClientRecord r = mActivities.get(token);
        if (!checkAndUpdateLifecycleSeq(seq, r, "resumeActivity")) {
            return;
        }

        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        // TODO Push resumeArgs into the activity for consideration
        r = performResumeActivity(token, clearHide, reason);

        if (r != null) {
            final Activity a = r.activity;
            ... ...
            // The window is now visible if it has been added, we are not
            // simply finishing, and we are not starting another activity.
            if (!r.activity.mFinished && willBeVisible
                    && r.activity.mDecor != null && !r.hideForNow) {
                ... ...
                if (r.activity.mVisibleFromClient) {
                    r.activity.makeVisible();
                }
            }
            ... ...

        } else {
            // If an exception was thrown when trying to resume, then
            // just end this activity.
            try {
                ActivityManager.getService()
                    .finishActivity(token, Activity.RESULT_CANCELED, null,
                            Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        }
    }

該方法的實(shí)現(xiàn)代碼比較多,我們只關(guān)注重點(diǎn)部分。由源碼可知,在獲取了Activity的Window相關(guān)參數(shù)之后,執(zhí)行了r.activity.makeVisible()方法,即接下來(lái)回去執(zhí)行Activity的makeVisible()方法,這個(gè)方法就是DecorView被添加到Window上去的起點(diǎn)。

二、Activity中的makeVisible方法

makeVisible字面上的意思是讓它變?yōu)榭梢?jiàn),我們看一下它的具體實(shí)現(xiàn):

    void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }

由源碼可知,這里的mWindowAdded是一個(gè)布爾類(lèi)型的成員變量,很明顯這里的主要邏輯if分支只會(huì)執(zhí)行一次,因?yàn)閳?zhí)行過(guò)后mWindowAdded就會(huì)被賦值為true,之后便再也不會(huì)執(zhí)行if分支代碼。緊接著,通過(guò)getWindowManager()方法獲取ViewManager對(duì)象,我們看一下具體實(shí)現(xiàn)。

    // Activity # getWindowManager()
    /** Retrieve the window manager for showing custom windows. */
    public WindowManager getWindowManager() {
        return mWindowManager;
    }

從方法的注釋來(lái)看是返回一個(gè)Window管理器,并且是以成員變量的形式返回。那么,很自然我們就要去尋找它是在哪邊被賦值的,通過(guò)代碼檢索,我們很容易發(fā)現(xiàn)它是在A(yíng)ctivity的attch()方法中被賦值的。

    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        ... ...
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;
        mWindow.setColorMode(info.colorMode);
    }

這邊我稍微精簡(jiǎn)了一下代碼,我們可以看到mWindowManager是通過(guò)PhoneWindow的getWindowManager()方法返回的。但是,通過(guò)查看源碼,我們可以發(fā)現(xiàn)PhoneWindow繼承于Window,而且沒(méi)有重寫(xiě)getWindowManager()方法,所以最終還是會(huì)調(diào)用到Window的getWindowManager()方法。

    // Window # getWindowManager()
    /**
     * Return the window manager allowing this Window to display its own
     * windows.
     *
     * @return WindowManager The ViewManager.
     */
    public WindowManager getWindowManager() {
        return mWindowManager;
    }

好吧,Window的getWindowManager()方法同樣是返回它的成員變量,所以我們依舊需要去找它是在哪邊被賦值的。這個(gè)還是比較好找的,最終我們可以發(fā)現(xiàn)它是在setWindowManager()方法中被賦值的。

    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

由源碼可知,mWindowManager最終是通過(guò)WindowManagerImpl的createLocalWindowManager()方法創(chuàng)建的。

    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }

可以看到,這個(gè)方法很簡(jiǎn)單,就是通過(guò)new的方式創(chuàng)建了一個(gè)WindowManagerImpl對(duì)象并返回。兜了這么一大圈,我們終于知道Activity的makeVisible()方法中g(shù)etWindowManager()返回的其實(shí)就是一個(gè)WindowManagerImpl對(duì)象。剛好,我們順便理一下繼承關(guān)系,就是WindowManage繼承自ViewManager,而WindowManagerImpl又繼承自WindowManage。

接下來(lái),我們繼續(xù)分析Activity的makeVisible()方法,在通過(guò)getWindowManager()方法獲取到WindowManagerImpl對(duì)象之后,接著執(zhí)行了如下方法:

wm.addView(mDecor, getWindow().getAttributes());

通過(guò)以上分析,我們很容易得知,這其實(shí)就是去調(diào)用WindowManagerImpl的addView方法,并且把DecorView作為入?yún)ⅰ?/p>

    // WindowManagerImpl # addView()
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

由源碼可知,mGlobal是一個(gè)WindowManagerGlobal的單例對(duì)象,是Window處理的工具類(lèi),用于操作View組件。我們具體看一下mGlobal的addView()方法的實(shí)現(xiàn):

    private final ArrayList<View> mViews = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();

    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        ... ...
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        ... ...
        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            ... ...
            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;
            }
        }
    }

由于代碼實(shí)現(xiàn)較長(zhǎng),稍微做了一下精簡(jiǎn),我們只關(guān)注重點(diǎn)部分。首先說(shuō)一下上述代碼中的3個(gè)ArrayList,其中mViews主要用于保存Activity的DecorView(Activity的根View),mRoots主要用于保存ViewRootImpl,mParams主要用于保存Window的LayoutParams。最后,調(diào)用了ViewRootImpl的setView()方法,這個(gè)方法就很關(guān)鍵了,將在下面進(jìn)行具體分析。

三、ViewRootImpl的setView()方法

在ViewRootImpl的setView()方法,會(huì)完成對(duì)View的測(cè)量、布局和繪制。并且,通過(guò)Binder跨進(jìn)程的方式向WMS(WindowManagerService)發(fā)起一個(gè)調(diào)用,從而將DecorView最終添加到Window上。在這個(gè)過(guò)程中,ViewRootImpl、DecorView和WMS會(huì)彼此向相互關(guān)聯(lián)。

我們先來(lái)看一下View的測(cè)量、布局和繪制過(guò)程,在ViewRootImpl的setView()方法中,會(huì)調(diào)用requestLayout()方法。

    // ViewRootImpl # setView()
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        ...
        requestLayout();
        ...
    }

下面我們看下requestLayout()方法的具體實(shí)現(xiàn):

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

    void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

我們可以看到,這里首先回去調(diào)用checkThread()方法,主要檢查當(dāng)前線(xiàn)程是否為UI線(xiàn)程,若當(dāng)前線(xiàn)程非UI線(xiàn)程,則拋出非UI線(xiàn)程更新UI的錯(cuò)誤。其中,mThread成員變量是在ViewRootImpl的構(gòu)造方法中被賦值的,而ViewRootImpl的構(gòu)造方法又是在UI線(xiàn)程中被調(diào)用的,所以mThread自然也就被賦值為UI線(xiàn)程。在完成UI線(xiàn)程檢查之后,接著會(huì)去調(diào)用scheduleTraversals()方法,我們看下它的具體實(shí)現(xiàn):

    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,內(nèi)部會(huì)調(diào)用一個(gè)異步消息,用于執(zhí)行mTraversalRunnable的run()方法,我們來(lái)具體看一下mTraversalRunnable的具體定義:

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

很明顯,最后后調(diào)用到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;
            }
        }
    }

我們可以看到,doTraversal()方法內(nèi)部又會(huì)去調(diào)用performTraversals()方法,其實(shí)這個(gè)方法就是整個(gè)View的繪制流程的起始方法,我們一起來(lái)看下,這個(gè)方法非常非常的長(zhǎng),我們只關(guān)注重點(diǎn)部分的代碼。

    private void performTraversals() {
        ... ...
        // View測(cè)量
            if (!mStopped || mReportNextDraw) {
                boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
                        (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
                if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
                        || mHeight != host.getMeasuredHeight() || contentInsetsChanged) {
                    int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
                    int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

                     // Ask host how big it wants to be
                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

                    // Implementation of weights from WindowManager.LayoutParams
                    // We just grow the dimensions as needed and re-measure if
                    // needs be
                    int width = host.getMeasuredWidth();
                    int height = host.getMeasuredHeight();
                    boolean measureAgain = false;

                    if (lp.horizontalWeight > 0.0f) {
                        width += (int) ((mWidth - width) * lp.horizontalWeight);
                        childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
                                MeasureSpec.EXACTLY);
                        measureAgain = true;
                    }
                    if (lp.verticalWeight > 0.0f) {
                        height += (int) ((mHeight - height) * lp.verticalWeight);
                        childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
                                MeasureSpec.EXACTLY);
                        measureAgain = true;
                    }

                    if (measureAgain) {
                        if (DEBUG_LAYOUT) Log.v(TAG,
                                "And hey let's measure once more: width=" + width
                                + " height=" + height);
                        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                    }

                    layoutRequested = true;
                }
            }
        }
        ... ...
        // View布局
        final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
        boolean triggerGlobalLayoutListener = didLayout
                || mAttachInfo.mRecomputeGlobalAttributes;
        if (didLayout) {
            performLayout(lp, desiredWindowWidth, desiredWindowHeight);

            // By this point all views have been sized and positioned
            // We can compute the transparent area

            if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
                // start out transparent
                // TODO: AVOID THAT CALL BY CACHING THE RESULT?
                host.getLocationInWindow(mTmpLocation);
                mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1],
                        mTmpLocation[0] + host.mRight - host.mLeft,
                        mTmpLocation[1] + host.mBottom - host.mTop);

                host.gatherTransparentRegion(mTransparentRegion);
                if (mTranslator != null) {
                    mTranslator.translateRegionInWindowToScreen(mTransparentRegion);
                }

                if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
                    mPreviousTransparentRegion.set(mTransparentRegion);
                    mFullRedrawNeeded = true;
                    // reconfigure window manager
                    try {
                        mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
                    } catch (RemoteException e) {
                    }
                }
            }
        }
        ... ...
        // View繪制
        if (!cancelDraw && !newSurface) {
            if (!skipDraw || mReportNextDraw) {
                if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
                    for (int i = 0; i < mPendingTransitions.size(); ++i) {
                        mPendingTransitions.get(i).startChangingAnimations();
                    }
                    mPendingTransitions.clear();
                }

                performDraw();
            }
        } else {
            if (viewVisibility == View.VISIBLE) {
                // Try again
                scheduleTraversals();
            } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
                for (int i = 0; i < mPendingTransitions.size(); ++i) {
                    mPendingTransitions.get(i).endChangingAnimations();
                }
                mPendingTransitions.clear();
            }
        }

        mIsInTraversal = false;
    }

由源碼可知,在performTraversals()方法中,主要是依次調(diào)用了performMeasure(),performLayout(),performDraw()這三個(gè)方法,用于完成View的測(cè)量、布局和繪制。

View的測(cè)量

我們一起先來(lái)看下performMeasure()方法的具體實(shí)現(xiàn)。

    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        if (mView == null) {
            return;
        }
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try {
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

我們看到,在該方法中調(diào)用了mView的measure()方法,通過(guò)源碼追蹤我們可以很容易發(fā)現(xiàn)這個(gè)成員變量mView其實(shí)就是傳遞進(jìn)來(lái)的DecorView對(duì)象,所以最終調(diào)用的是DecorView的measure()方法,我們具體看下。

    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
        boolean optical = isLayoutModeOptical(this);
        if (optical != isLayoutModeOptical(mParent)) {
            ... ...
        if (forceLayout || needsLayout) {
            ... ...
            int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
            if (cacheIndex < 0 || sIgnoreMeasureCache) {
                // measure ourselves, this should set the measured dimension flag back
                onMeasure(widthMeasureSpec, heightMeasureSpec);
                mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
            } else {
                long value = mMeasureCache.valueAt(cacheIndex);
                // Casting a long to int drops the high 32 bits, no mask needed
                setMeasuredDimensionRaw((int) (value >> 32), (int) value);
                mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
            }

            mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
        }
        ... ...
    }

我們可以看到,measure方法主要是做一些判斷,如果View需要測(cè)量就會(huì)去調(diào)用它的onMeasure()方法。下面,我們就來(lái)看一下DecorView的onMeasure()方法。

    // DecorView # onMeasure()
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int widthMode = getMode(widthMeasureSpec);
        final int heightMode = getMode(heightMeasureSpec);
        ... ...
        boolean measure = false;
        ... ...
        if (!fixedWidth && widthMode == AT_MOST) {
            ... ...
                if (width < min) {
                    widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY);
                    measure = true;
                }
            }
        }

        if (measure) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }

我們可以看到,DecorView的onMeasure()方法主要是用來(lái)判斷DecorView需不需要測(cè)量,如果需要測(cè)量,則調(diào)用其父類(lèi)FrameLayout的onMeasure()方法。

    // FrameLayout # onMeasure()
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int count = getChildCount();

        final boolean measureMatchParentChildren =
                MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
                MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
        mMatchParentChildren.clear();

        int maxHeight = 0;
        int maxWidth = 0;
        int childState = 0;

        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (mMeasureAllChildren || child.getVisibility() != GONE) {
                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                maxWidth = Math.max(maxWidth,
                        child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
                maxHeight = Math.max(maxHeight,
                        child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
                childState = combineMeasuredStates(childState, child.getMeasuredState());
                if (measureMatchParentChildren) {
                    if (lp.width == LayoutParams.MATCH_PARENT ||
                            lp.height == LayoutParams.MATCH_PARENT) {
                        mMatchParentChildren.add(child);
                    }
                }
            }
        }
        ... ...
        count = mMatchParentChildren.size();
        if (count > 1) {
            for (int i = 0; i < count; i++) {
                final View child = mMatchParentChildren.get(i);
                final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
                final int childWidthMeasureSpec;
                ... ...
                final int childHeightMeasureSpec;
                ... ...
                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
            }
        }
    }

由源碼可知,這里主要通過(guò)for循環(huán),獲取該View的所有子View,并執(zhí)行所有子View的measure方法,如果子View是ViewGroup就會(huì)調(diào)用ViewGroup的onMeasure()方法,若果子View是View就會(huì)調(diào)用View的onMeasure()方法。因此,我們還需要看一下View的onMeasure()方法。

    // View # onMeasure()
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }
    protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
        boolean optical = isLayoutModeOptical(this);
        if (optical != isLayoutModeOptical(mParent)) {
            Insets insets = getOpticalInsets();
            int opticalWidth  = insets.left + insets.right;
            int opticalHeight = insets.top  + insets.bottom;

            measuredWidth  += optical ? opticalWidth  : -opticalWidth;
            measuredHeight += optical ? opticalHeight : -opticalHeight;
        }
        setMeasuredDimensionRaw(measuredWidth, measuredHeight);
    }
    private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
        mMeasuredWidth = measuredWidth;
        mMeasuredHeight = measuredHeight;

        mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
    }

這樣,通過(guò)一系列的方法調(diào)用就可以把View即其子View的大小測(cè)量出來(lái)了,并且保存在了成員變量mMeasuredWith和mMeasuredHeight中。

View的布局

回到performTraversals()方法中,我們繼續(xù)看performLayout()方法。

    private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
            int desiredWindowHeight) {
        ... ...
        final View host = mView;
        if (host == null) {
            return;
        }
        try {
            host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
            ... ...
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        mInLayout = false;
    }

我們可以看到,該方法中主要調(diào)用了host的layout()方法,而host又是通過(guò)mView(DecorView)賦值的,所以就是去調(diào)用DecorView的layout()方法。通過(guò)源碼可以發(fā)現(xiàn)最終調(diào)用的是ViewGroup的layout()方法。

    // ViewGroup # layout()
    @Override
    public final void layout(int l, int t, int r, int b) {
        if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
            if (mTransition != null) {
                mTransition.layoutChange(this);
            }
            super.layout(l, t, r, b);
        } else {
            // record the fact that we noop'd it; request layout when transition finishes
            mLayoutCalledWhileSuppressed = true;
        }
    }

好吧,這里又調(diào)用了其父類(lèi)的layout()方法,所以又會(huì)去調(diào)用View的layout()方法。

    public void layout(int l, int t, int r, int b) {
        ... ...
        if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
            onLayout(changed, l, t, r, b);
            ... ...
        }
    }

我們可以看到,layout()方法主要還是做一些判斷,如果View有變化需要重新布局,則會(huì)去調(diào)用它的onLayout()方法。所以,接下來(lái)我們需要看一下DecorView的onLayout()方法。

    // DecorView # onLayout()
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        getOutsets(mOutsets);
        if (mOutsets.left > 0) {
            offsetLeftAndRight(-mOutsets.left);
        }
        if (mOutsets.top > 0) {
            offsetTopAndBottom(-mOutsets.top);
        }
        if (mApplyFloatingVerticalInsets) {
            offsetTopAndBottom(mFloatingInsets.top);
        }
        if (mApplyFloatingHorizontalInsets) {
            offsetLeftAndRight(mFloatingInsets.left);
        }

        // If the application changed its SystemUI metrics, we might also have to adapt
        // our shadow elevation.
        updateElevation();
        mAllowUpdateElevation = true;

        if (changed && mResizeMode == RESIZE_MODE_DOCKED_DIVIDER) {
            getViewRootImpl().requestInvalidateRootRenderNode();
        }
    }

我們看到這個(gè)方法中又會(huì)去調(diào)用其父類(lèi)Framelayout的onLayout()方法。

    // Framelayout # onLayout()
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        layoutChildren(left, top, right, bottom, false /* no force left gravity */);
    }

    void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
        final int count = getChildCount();

        final int parentLeft = getPaddingLeftWithForeground();
        final int parentRight = right - left - getPaddingRightWithForeground();

        final int parentTop = getPaddingTopWithForeground();
        final int parentBottom = bottom - top - getPaddingBottomWithForeground();

        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                ... ...
                child.layout(childLeft, childTop, childLeft + width, childTop + height);
            }
        }
    }

Framelayout的onLayout()方法又會(huì)去調(diào)用layoutChildren()方法,具體代碼上面已經(jīng)貼出來(lái)了。與measure類(lèi)似,這里也是通過(guò)for循環(huán)獲取該View的所有子View,如果子View是ViewGroup則執(zhí)行ViewGroup的layout()方法,如果子View是View則執(zhí)行View的layout()方法。
這樣,通過(guò)一系列的方法調(diào)用就可以把View及其子View的位置信息計(jì)算出來(lái)并保存在成員變量中。

View的繪制

好了,我們?cè)俅位氐絧erformTraversals()方法中,繼續(xù)看performDraw()方法。

    private void performDraw() {
        ... ...
        draw(fullRedrawNeeded);
        ... ...
    }
    private void draw(boolean fullRedrawNeeded) {
            ... ...
            if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
               return;
            }
            ... ...
    }
    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty) {

        ... ...
        mView.draw(canvas);
        ... ...
        return true;
    }

對(duì)源碼做了大量的精簡(jiǎn),我們看到最后會(huì)輾轉(zhuǎn)調(diào)用到mView的draw()方法,也就是調(diào)用DecorView的draw()方法。

    // DecorView # draw()
    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);

        if (mMenuBackground != null) {
            mMenuBackground.draw(canvas);
        }
    }

DecorView的draw()方法又會(huì)去調(diào)用其父類(lèi)FrameLayout的draw()方法,最終又會(huì)調(diào)用到View的draw()方法,一起來(lái)看下。

    // View # draw()
    @CallSuper
    public void draw(Canvas canvas) {
        final int privateFlags = mPrivateFlags;
        final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
        mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;

        /*
         * Draw traversal performs several drawing steps which must be executed
         * in the appropriate order:
         *
         *      1. Draw the background
         *      2. If necessary, save the canvas' layers to prepare for fading
         *      3. Draw view's content
         *      4. Draw children
         *      5. If necessary, draw the fading edges and restore layers
         *      6. Draw decorations (scrollbars for instance)
         */

        // Step 1, draw the background, if needed
        int saveCount;

        if (!dirtyOpaque) {
            drawBackground(canvas);
        }

        // skip step 2 & 5 if possible (common case)
        final int viewFlags = mViewFlags;
        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
        if (!verticalEdges && !horizontalEdges) {
            // Step 3, draw the content
            if (!dirtyOpaque) onDraw(canvas);

            // Step 4, draw the children
            dispatchDraw(canvas);

            drawAutofilledHighlight(canvas);

            // Overlay is part of the content and draws beneath Foreground
            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().dispatchDraw(canvas);
            }

            // Step 6, draw decorations (foreground, scrollbars)
            onDrawForeground(canvas);

            // Step 7, draw the default focus highlight
            drawDefaultFocusHighlight(canvas);

            if (debugDraw()) {
                debugDrawFocus(canvas);
            }

            // we're done...
            return;
        }

        /*
         * Here we do the full fledged routine...
         * (this is an uncommon case where speed matters less,
         * this is why we repeat some of the tests that have been
         * done above)
         */

        boolean drawTop = false;
        boolean drawBottom = false;
        boolean drawLeft = false;
        boolean drawRight = false;

        float topFadeStrength = 0.0f;
        float bottomFadeStrength = 0.0f;
        float leftFadeStrength = 0.0f;
        float rightFadeStrength = 0.0f;

        // Step 2, save the canvas' layers
        int paddingLeft = mPaddingLeft;

        final boolean offsetRequired = isPaddingOffsetRequired();
        if (offsetRequired) {
            paddingLeft += getLeftPaddingOffset();
        }

        int left = mScrollX + paddingLeft;
        int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
        int top = mScrollY + getFadeTop(offsetRequired);
        int bottom = top + getFadeHeight(offsetRequired);

        if (offsetRequired) {
            right += getRightPaddingOffset();
            bottom += getBottomPaddingOffset();
        }

        final ScrollabilityCache scrollabilityCache = mScrollCache;
        final float fadeHeight = scrollabilityCache.fadingEdgeLength;
        int length = (int) fadeHeight;

        // clip the fade length if top and bottom fades overlap
        // overlapping fades produce odd-looking artifacts
        if (verticalEdges && (top + length > bottom - length)) {
            length = (bottom - top) / 2;
        }

        // also clip horizontal fades if necessary
        if (horizontalEdges && (left + length > right - length)) {
            length = (right - left) / 2;
        }

        if (verticalEdges) {
            topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
            drawTop = topFadeStrength * fadeHeight > 1.0f;
            bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
            drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
        }

        if (horizontalEdges) {
            leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
            drawLeft = leftFadeStrength * fadeHeight > 1.0f;
            rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
            drawRight = rightFadeStrength * fadeHeight > 1.0f;
        }

        saveCount = canvas.getSaveCount();

        int solidColor = getSolidColor();
        if (solidColor == 0) {
            final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;

            if (drawTop) {
                canvas.saveLayer(left, top, right, top + length, null, flags);
            }

            if (drawBottom) {
                canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
            }

            if (drawLeft) {
                canvas.saveLayer(left, top, left + length, bottom, null, flags);
            }

            if (drawRight) {
                canvas.saveLayer(right - length, top, right, bottom, null, flags);
            }
        } else {
            scrollabilityCache.setFadeColor(solidColor);
        }

        // Step 3, draw the content
        if (!dirtyOpaque) onDraw(canvas);

        // Step 4, draw the children
        dispatchDraw(canvas);

        // Step 5, draw the fade effect and restore layers
        final Paint p = scrollabilityCache.paint;
        final Matrix matrix = scrollabilityCache.matrix;
        final Shader fade = scrollabilityCache.shader;

        if (drawTop) {
            matrix.setScale(1, fadeHeight * topFadeStrength);
            matrix.postTranslate(left, top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(left, top, right, top + length, p);
        }

        if (drawBottom) {
            matrix.setScale(1, fadeHeight * bottomFadeStrength);
            matrix.postRotate(180);
            matrix.postTranslate(left, bottom);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(left, bottom - length, right, bottom, p);
        }

        if (drawLeft) {
            matrix.setScale(1, fadeHeight * leftFadeStrength);
            matrix.postRotate(-90);
            matrix.postTranslate(left, top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(left, top, left + length, bottom, p);
        }

        if (drawRight) {
            matrix.setScale(1, fadeHeight * rightFadeStrength);
            matrix.postRotate(90);
            matrix.postTranslate(right, top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(right - length, top, right, bottom, p);
        }

        canvas.restoreToCount(saveCount);

        drawAutofilledHighlight(canvas);

        // Overlay is part of the content and draws beneath Foreground
        if (mOverlay != null && !mOverlay.isEmpty()) {
            mOverlay.getOverlayView().dispatchDraw(canvas);
        }

        // Step 6, draw decorations (foreground, scrollbars)
        onDrawForeground(canvas);

        if (debugDraw()) {
            debugDrawFocus(canvas);
        }
    }

我們可以看到,View的整個(gè)繪制流程還是比較清楚的,整個(gè)執(zhí)行邏輯一共大概需要六步,并且在執(zhí)行draw()方法的過(guò)程中,如果包含子View,那么也會(huì)執(zhí)行子View的draw()方法。這樣,經(jīng)過(guò)一系列的方法調(diào)用之后,DectorView及其子View就被繪制出來(lái)了。
至此,ViewRootImpl的setView()方法中的requestLayout()部分的分析差不多已經(jīng)結(jié)束了。

四、DecorView添加到Window上

我們回到ViewRootImpl的setView()方法中,requestLayout()方法調(diào)用完畢之后,View的測(cè)量、布局和繪制已經(jīng)完成了,下面就是要把DecorView真正添加到Window上去了。

    // ViewRootImpl # setView()
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        ...
        requestLayout();
        try {
            mOrigWindowType = mWindowAttributes.type;
            mAttachInfo.mRecomputeGlobalAttributes = true;
            collectViewAttributes();
            res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                      getHostVisibility(), mDisplay.getDisplayId(),
                      mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                      mAttachInfo.mOutsets, 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);
        } finally {
            if (restore) {
                attrs.restore();
            }
        }
        ...
    }

我們可以看到,通過(guò)mWindowSession的addToDisplay()方法調(diào)用,將DecorView添加到Window上去。我們先看一下mWindowSession是如何賦值的。

    mWindowSession = WindowManagerGlobal.getWindowSession();
    public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance();
                    IWindowManager windowManager = getWindowManagerService();
                    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;
        }
    }
    public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
                try {
                    if (sWindowManagerService != null) {
                        ValueAnimator.setDurationScale(
                                sWindowManagerService.getCurrentAnimatorScale());
                    }
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowManagerService;
        }
    }

熟悉Binder跨進(jìn)程通信的同學(xué)應(yīng)該知道,最后應(yīng)該是通過(guò)WindowManagerService的openSession()方法獲取到了mWindowSession對(duì)象。

    @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;
    }

我們看到openSession()方法通過(guò)new的方式返回了一個(gè)IWindowSession類(lèi)型的對(duì)象。
回到ViewRootImpl的setView()方法,我們看到它調(diào)用了mWindowSession的addToDisplay()方法,這里也就是調(diào)用了Session的addToDisplay()方法。

    // Session # addToDisplay()
    @Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
            Rect outOutsets, InputChannel outInputChannel) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outOutsets, outInputChannel);
    }

由源碼可知,這里的成員變量mService是WindowManagerService對(duì)象,所以最終會(huì)去調(diào)用WindowManagerService的addWindow()方法。因此,最終是由WindowManagerService來(lái)完成將DecorView添加到Window上。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 金陵現(xiàn)江蘇南京,明朝時(shí)期極盡奢華的地方,蘇州山塘 金陵秦淮河不同凡響的藝術(shù)修養(yǎng)與卑賤的身份混合于一體,構(gòu)成了封建社...
    酒青梅閱讀 1,796評(píng)論 0 0
  • 總是猜不透,我在想什么。 其實(shí),我一直很茫然。 自己到底在追求什么。 到現(xiàn)在才明白。 原來(lái) 。 是空白。 是否是記...
    米小姐Y閱讀 271評(píng)論 0 2
  • 以前每次讀完一本書(shū)或者看完一部電影,就會(huì)在豆瓣搜書(shū)評(píng)或影評(píng),仿佛看了那些評(píng)論,就覺(jué)得那是我自己對(duì)書(shū)或電影的理解和感...
    薄荷的夢(mèng)想天空閱讀 2,610評(píng)論 1 0

友情鏈接更多精彩內(nèi)容