ViewRootImpl入門

基礎

        它本身只是一個很平常的類(據說早期的ViewRootImpl是一個Handler,然而api23中它便不是)。
        在Activity#handleResumeActivity()中,會將Activity所關聯的PhoneWindow對象中的DecorView傳遞給ViewRootImpl#setView()中。而由ViewRootImpl對該View進行測量、佈局與繪製,同時ViewRootImpl複雜與WMS交互,將Activity的UI給展示出來。總結一下,它的作用就是:
        1,爲View進行填充,即將Activity#setContentView()生成的乾癟的View樹給真正的繪製出來。
        2,與WMS進行溝通,管理要顯示的界面。

構造函數

        ActivityThread#handleResumeActivity()->WindowManagerImpl#addView()->WindowManagerGlobal#addView()->ViewRootImpl(view.getContext(),Display)。

        構造函數中會將第一個參數賦值給ViewRootImpl的全局變量mContext。handleResumeActivity傳入的View對象是Window#mDecor,所以view.getContext()返回的就是DecorView所指的Activity實例。

        擇一部分構造函數中的代碼如下:

        mWindowSession = WindowManagerGlobal.getWindowSession();
        mWindow = new W(this);
        mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this);

        第一句mWindowSession賦值爲WindowManagerGlobal.getWindowSession(),其返回值爲一個Session對象,Session的實例化在WMS進程中進行的,本應該中有一個Session的代理對象,所以可以通過Session主要調用WMS中一些方法。

        第二、三句中的W繼承於IWindow.Stub,後者繼承於Binder又實現了IWindow接口,因此這個W是可以IPC的。第三句中將mWindow賦值給了View.AttachInfo中的mWindow對象,將mWindowSession賦值給了mSession變量。

setView

        與構造函數調用時機相同,並且View參數也相同。所以ViewRootImpl中的mView指的是該ViewRootImpl指依賴的Activity中的DecorView。因此它可以通過調用mView來繪製一個Acitivity組件的UI。

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                //略
                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);
                } 
                //略
            }
        }
    }

        而requestLayout->scheduleTraversals()->Choreographer#postCallback()->……->postCallbackDelayedInternal()->scheduleFrameLocked()->scheduleFrameLocked(),它發一個what爲MSG_DO_FRAME的msg->doFrame()。
        在這個過程中,有必要跟蹤一下scheduleTraversals()的參數,在postCallbackDelayedInternal()之前,都是正常傳遞的。postCallbackDelayedInternal()如下:

    private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long delayMillis) {//前兩個參數就是scheduleTraversals()中的參數,token爲Null,delayMills爲0
        synchronized (mLock) {
            final long now = SystemClock.uptimeMillis();
            final long dueTime = now + delayMillis;
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
            //上一步將三個參數封裝成CallbackRecord對象,並通過隊列存儲
            if (dueTime <= now) {//由於delayMills爲0,所以走這一步
                scheduleFrameLocked(now);
            } else {
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, dueTime);
            }
        }
    }

到doFrame()時,有這麼一句話:

            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
其中第一個參數就是scheduleTraversals()的第一個參數,而doCallBacks()中又會調用CallbackRecord#run()。
        public void run(long frameTimeNanos) {
            if (token == FRAME_CALLBACK_TOKEN) {
                ((FrameCallback)action).doFrame(frameTimeNanos);
            } else {
                ((Runnable)action).run();
            }
        }
        因此到這裏之後,會執行scheduleTraversals()中的第二個參數。其第二個參數->doTraversal()->performTraversals(),在這個方法中會執行View的measure,layout與draw。
        到這裏,requestLayout()方法執行完畢,其作用就是將View樹測量、佈局與繪製。
        而在setView()中,還調用了mWindowSession#addToDisplay(),由於mWindowSession是WMS創建的Session對象在本應用中的一個代理,所以mWindowSession#addToDisplay()便會執行到WMS所在的進程中。而Session中該方法很簡單,只是添加一個參數(this指代的是Session對象自身)然後傳到WMS#addWindow()中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章