Android 頂級視圖 DecorView 的前世今生

來源:互聯網
上載者:User

標籤:session   group   host   spec   end   sum   自訂view   ram   情況   

在Activity的啟動過程中會執行ActivityThread#performLaunchActivity方法,其中調用Activity#attach。在attach()方法中執行個體化Activity持有的mWindow屬性為Window的唯一實作類別PhoneWindow。

    ActivityThread#performLaunchActivity    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {        ...        activity.attach(...);        ...    }    Activity#attach    final void attach(...) {        ...        mWindow = new PhoneWindow(this, window);        mWindow.setWindowManager(...);        mWindowManager = mWindow.getWindowManager();        ...    }

在ActivityThread#handleResumeActivity中,通過ActivityThread#performResumeActivity方法調用Activity#onResume之後,將會擷取DecorView並通過WindowManager添加進ViewRootImpl。

    final void handleResumeActivity(...) {        ...        r = performResumeActivity(token, clearHide, reason);        r.window = r.activity.getWindow();        View decor = r.window.getDecorView();        decor.setVisibility(View.INVISIBLE);        ViewManager wm = a.getWindowManager();        a.mDecor = decor;        wm.addView(decor, l);        ...        r.activity.makeVisible();        ...    }     PhoneWindow#getDecorView     public final View getDecorView() {        if (mDecor == null || mForceDecorInstall) {            installDecor();        }        return mDecor;    }

r.activity.getWindow()返回的就是在Activity#attach中執行個體化的PhoneWindow對象。之後調用PhoneWindow#getDecorView去擷取DecorView對象,具體過程參見 Android setContentView()源碼解析。然後填充decorView但是設定為不可見(通過r.activity.makeVisible()設定可見),最後通過WindowManager#addView將decorView添加進ViewRootImpl。ViewRootImpl下文會仔細分析,關於addView的過程參考Android 使用WindowManager實現懸浮窗及源碼解析。細心的同學可能會注意到這時的DecorView剛通過WindowManger#addView添加到ViewRoot,其實這也解釋了為什麼說“在onCreate至onResume過程中,Activity已經對系統可見,但是還沒有展示到介面上”的原因。這時因為Activity被裝載而且已經執行完attach、onCreate、onStart、甚至執行完onResume,但是DecorView始終沒有被添加到Window上。onCreate、onStart、onResume中擷取不到控制項寬高也是因為這個原因,在WindowManager#addView(內部執行個體化ViewRoot)之後才會真正的執行layout、measure、draw。

DecorView是FrameLayout的子類,說白了也是繼承自View。其餘XML中的View/ViewGroup都是被添加進DecorView屬於“將View/ViewGroup添加進ViewGroup”類型,不具有代表性,感興趣的可以參考Android XML布局檔案解析過程源碼解析。

如果在Activity#onCreate中調用setContentView()方法,那麼會直接建立DecorView。如果沒調用setContentView()方法,那麼在ActivityThread#handleResumeActivity中會通過r.window.getDecorView()自動建立DecorView。總之,不管你建立於否,Activity中總會存在由PhoneWindow建立的DecorView。PhoneWindow的作用就是操作View,Activity調用findViewById類似的方法都是通過PhoneWindow間接操作View。如此,DecorView作為View樹的頂級視圖通過PhoneWindow便和Activity關聯了起來。

在WindowManger#addView的過程中,調用了ViewRootImpl#addView。

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {        synchronized (this) {            if (mView == null) {                ...                mView = view;                requestLayout();                res = mWindowSession.addToDisplay(...);                ...                }            }    }

首先將傳遞進來的View賦值給全域屬性mView,之後用mView屬性來操作DecorView。mWindowSession.addToDisplay的過程在Android 使用WindowManager實現懸浮窗及源碼解析中已經解析過,簡述作用就是將window添加進WindowManagerService的mWindowMap屬性進行管理。requestLayout()這個方法就厲害了,可能你會自訂View,可能你看過View繪製的三大流程(measure、layout、draw),可是你知道這些東西的源頭嗎?沒錯,都在requestLayout()這裡。插個題外話,通過上述的代碼可以發現,PhoneWindow也不是直接操作DecorView,中間還隔著個ViewRootImpl。

    @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.");        }    }

通常在子線程中更新UI,會爆出checkThread中的錯誤。但是仔細看這個方法,mThread是在初始化ViewRootImpl時執行個體化的,等於建立ViewRootImpl的線程。正常情況下我們是在主線程建立的ViewRootImpl,實際上在子線程中也是可以建立的,例如建立Toast。Toast也是一種window,參考Android 進階自訂Toast及源碼解析。但是Thread.currentThread()為更新UI時的線程。這兩個如果同時在子線程會怎麼樣呢?什麼也不會發生。是的,子線程是可以更新UI的。但是這裡對UI的建立線程有要求,只有子線程建立的ViewRootImpl可以在子線程中更新UI。子線程更新UI的範例程式碼如下:

        new Thread(new Runnable() {            @Override            public void run() {                Looper.prepare();                // Toast.makeText(MainActivity.this,"一口仨饃",Toast.LENGTH_SHORT).show();                new AlertDialog.Builder(MainActivity.this)                        .setTitle("Game of Thrones")                        .setMessage("winter is coming")                        .setPositiveButton("yes,my lord", null)                        .show();                Looper.loop();            }        }).start();

scheduleTraversals()方法經過層層調用(mTraversalRunnable->doTraversal->performTraversals)

    private void performTraversals() {        final View host = mView;        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);        performLayout(lp, mWidth, mHeight);        performDraw();    }    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);    }     private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,            int desiredWindowHeight) {         final View host = mView;         host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());           }    private void performDraw() {        // draw(fullRedrawNeeded) --> drawSoftware        mView.draw(canvas);    }

performTraversals方法巨長,這裡只截取View繪製三大流程的起點。mView就是之前緩衝的DecorView。之後便開始了View的measure、layout、draw、onMeasure、onLayout、ondraw。。。

更多Framework源碼解析,請移步 Framework源碼解析系列[目錄]

Android 頂級視圖 DecorView 的前世今生

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.