從源碼的角度分析View的繪制流程

從源碼的角度分析View的繪制流程

溫馨提示:以下源碼分析會截取源碼進(jìn)行說明,部分方法源碼較長會只保留關(guān)鍵代碼,其他代碼用...省略

為了便于大家更好的理解源碼調(diào)用邏輯,在看源碼之前我們先來看下源碼調(diào)用流程圖


image.png

View的繪制入口是ActivityThread#handleResumeActivity

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
            ...
            
            // TODO Push resumeArgs into the activity for consideration
            // 回調(diào)Activity的onResume()
            r = performResumeActivity(token, clearHide, 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();
            
            // 初始化窗口布局屬性對象
            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;
        
                ViewRootImpl impl = decor.getViewRootImpl();
                if (impl != null) {
                    impl.notifyChildRebuilt();
                }
            }
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    // 注釋1
                    wm.addView(decor, l);
                } else {
                    a.onWindowAttributesChanged(l);
                }
            }

        
        } else if (!willBeVisible) {
            if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
            r.hideForNow = true;
        }
             
}

可以看到其中又調(diào)用了performResumeActivity,此方法會回調(diào)Activity的onResume(),注釋1處調(diào)用了wm.addView(decor, l)傳入DecorView和窗口的布局參數(shù),此處的wm是一個接口ViewManager,是通過a.getWindowManager()賦值的,通過這個方法可知wm是ViewManager的實(shí)現(xiàn)類WindowManager,此處的a是Activity對象,跟進(jìn)Activity#getWindowManager

public WindowManager getWindowManager() {
        return mWindowManager;
}

直接返回一個WindowManager對象,繼續(xù)查找該對象在哪里賦值的

 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) {
            
            ...
            
             mWindowManager = mWindow.getWindowManager();
            
            ...
}

可以看到是在Activity的attach()中賦值,繼續(xù)跟進(jìn)Window#getWindowManager發(fā)現(xiàn),依舊是返回一個mWindowManager,找到mWindowManager賦值處

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

可以看到,調(diào)用WindowManagerImpl的createLocalWindowManager來初始化mWindowManager,繼續(xù)跟進(jìn)WindowManagerImpl#createLocalWindowManager

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

可以看到返回值是WindowManagerImpl,那么之前分析的在handleResumeActivity()中調(diào)用了vm.addView中的vm是WindowManagerImpl的對象,接著找到WindowManagerImpl#addView

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

可以看到調(diào)用了mGlobal的addView方法,mGlobal是WindowManagerGlobal對象,繼續(xù)跟進(jìn)WindowManagerGlobal#addView

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 {
                // 注釋2
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

這里創(chuàng)建了一個ViewRootImpl的對象root,并調(diào)用ViewRootImpl#setView,將DecorView傳入進(jìn)去,繼續(xù)跟進(jìn)ViewRootImpl#setView發(fā)現(xiàn)里面調(diào)用了requestLayout()

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

可以看到里面調(diào)用了一個checkThread()方法,主要用來檢查當(dāng)前的繪制過程是否是在主線程執(zhí)行

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

繼續(xù)看requestLayout代碼,接著又調(diào)用了scheduleTraversals()

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

可看到代碼中調(diào)用了mChoreographer的postCallback方法,里面?zhèn)魅肓艘粋€mTraversalRunnable,跟進(jìn)這個mTraversalRunnable

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

最后會調(diào)用TraversalRunnable里面的run(),run()里面調(diào)用了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里面接著調(diào)用了 performTraversals方法,performTraversals源碼太長,就不貼出來了,里面調(diào)用了繪制的三大步驟:performMeasure、performLayout、performDraw,本篇文檔先分析到此,后續(xù)會繼續(xù)分析performMeasure、performLayout、performDraw具體源碼

總結(jié)下源碼調(diào)用邏輯
ActivityThread.H.handleMessage(處理Activity生命周期)
-> ActivityThread#handleLaunchActivity()
-> ActivityThread#performLaunchActivity()
-> ActivityThread#handleResumeActivity()
-> ActivityThread#performResumeActivity() (回調(diào)Activity的生命周期onResume())
-> WindowManagerImpl#addView(decorView,layoutParams)
-> WindowManagerGlobal#addView:
root = new ViewRootImpl()
root.setView()
-> ViewRootImpl#requestLayout() (會檢查當(dāng)前線程是否是主線程checkThread)
-> ViewRootImpl#scheduleTraversals()
mChoreographer.postCallback(xx,mTraversalRunnable,xx)
-> ViewRootImpl#TraversalRunnable.run()
-> ViewRootImpl#doTraversal()
-> ViewRootImpl#performTraversals()
performMeasure()
performLayout()
performDraw()

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

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

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