十二竹揍、Window、Activity邪铲、DecorView鬼佣、ViewRoot之間的關(guān)系

1.簡介

Activity

Activity并不負(fù)責(zé)視圖控制,它只是控制生命周期和處理事件霜浴。真正控制試圖的是Window。一個(gè)Activity包含了一個(gè)Window蓝纲,Window才是真正的代表一個(gè)窗口阴孟。Activity就像一個(gè)控制器,統(tǒng)籌視圖的添加與顯示税迷,以及通過其他回調(diào)方法永丝,來與Window、以及View進(jìn)行交互箭养。

DecorView

DecorView是Fragment的子類慕嚷,它可以被認(rèn)為是Android視圖樹的根節(jié)點(diǎn)視圖。DecorView作為頂級(jí)View毕泌,一般情況下它內(nèi)部包含一個(gè)樹直方向的LinearLayout喝检,這個(gè)LinearLayout里面上下包含3個(gè)部分,上面是ViewStub撼泛,延遲加載的視圖(應(yīng)該是設(shè)置ActionBar挠说,根據(jù)Theme設(shè)置),中間的是標(biāo)題欄(根據(jù)Theme設(shè)置愿题,有的布局沒有)损俭,下面的是內(nèi)容欄蛙奖。例如:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:orientation="vertical"
        android:layout_height="match_parent">

    <ViewStub
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>
    </FrameLayout>

    <FrameLayout
            android:id="@+id/content"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    </FrameLayout>
</LinearLayout>

在Activity中通過setContentView所設(shè)置的布局文件其實(shí)就是被加載到內(nèi)容欄之中的,成為其唯一子View杆兵,就是上面的id為content的FrameLayout中雁仲,在代碼中可以通過content來得到對(duì)應(yīng)加載的布局。

    ViewGroup content = findViewById(R.id.content);
    ViewGroup rootView = (ViewGroup) content.getChildAt(0)
ViewRoot

所有View的繪制以及事件分發(fā)等交互都是通過它里執(zhí)行或傳遞的琐脏。
ViewRoot對(duì)應(yīng)的ViewRootImpl類攒砖,它是連接WindowManagerService和DecorView的紐帶,View的三大流程(測量(measure)骆膝,布局(layout)祭衩,繪制(draw))均通過ViewRoot來完成。
ViewRoot并不屬于View樹的一份子阅签。從源碼的實(shí)現(xiàn)上看掐暮,它既非View的子類,也非View的父類政钟,但是它實(shí)現(xiàn)了ViewParent接口路克,這個(gè)可以作為View的名義上的父視圖,RootView繼承了Hanld類养交,可以接收事件分發(fā)精算,Android的所有的觸屏事件、按鍵事件碎连、界面刷新事件都是通過ViewRoot來進(jìn)行分發(fā)的灰羽。
他們之間的關(guān)系:


關(guān)系圖

2.DecorView的創(chuàng)建

在Activity中的setContentView();

public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

可以看出實(shí)際上是交給交給window裝載視圖。
在attach方法中鱼辙,

    @UnsupportedAppUsage
    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, IBinder assistToken) {
 ····················································
      //創(chuàng)建一個(gè)window的對(duì)象
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
    //設(shè)置回調(diào)廉嚼,向Activity分發(fā)點(diǎn)擊或者狀態(tài)變化事件
        mWindow.setCallback(this);
  ··············································
  //給Window設(shè)置WindowManager對(duì)象
      mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
  ······················································ 
    }

在Activity中的attach()方法中,生成了PhoneWindow實(shí)例倒戏。

     public void setContentView(int layoutResID) {
        //mContentParent為空怠噪, 創(chuàng)建一個(gè)DecroView
        if (mContentParent == null) {
            installDecor();
        } else {
            //mContentParent不為空, 刪除其中的View
            mContentParent.removeAllViews();
        }
        //為mContentParent添加子View, 即Activity中設(shè)置的布局文件
        mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            //回調(diào)通知杜跷, 內(nèi)容改變
            cb.onContentChanged();
        }
    }

其中的mContentParent指的是布局中的 @android:id/content 所對(duì)應(yīng)的FrameLayout傍念。

上述流程中,大致可以了解先在PhoneWindow中創(chuàng)建了一個(gè)DecroView葛闷,其中創(chuàng)建過程中憋槐,可能根據(jù)Theme不同,加載不同的布局格式淑趾,例如有沒有Title秦陋,有沒有設(shè)置ActionBar等,然后再向mContentParent中中加入View治笨,即Activity中設(shè)置的布局驳概。到此赤嚼,視圖一層層嵌套添加上了。

DecorView的顯示

通過setContentView設(shè)置的界面顺又,為什么在onResum()之后才對(duì)用戶可見更卒?這個(gè)需要從ActivityThread開始說起。

   private void handleLaunchActivity(ActivityClientRecord r, Intent  customIntent) {
        //就是在這里調(diào)用了Activity.attach()呀稚照, 接著調(diào)用了Activity.onCreat e() 和Activity.onStart() 生命周期蹂空,
        //但是由于只是初始化了mDecor, 添加了布局文件果录, 還沒有把
        //mDecor添加到負(fù)責(zé)UI顯示的PhoneWindow中上枕, 所以這時(shí)候?qū)τ脩魜碚f, 是不可見的
        Activity a = performLaunchActivity(r, customIntent);
        ......
        if (a != null) {
            //這里面執(zhí)行了Activity.onResume()
            handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotRes umed);
            if (!r.activity.mFinished && r.startsNotResumed) {
                try {
                    r.activity.mCalled = false;
                    //執(zhí)行Activity.onPause()
                    mInstrumentation.callActivityOnPause(r.activity);
                }  
            }
        }
    }

主要在handleResumeActivity方法中弱恒,在這其中辨萍,DecorView將會(huì)顯示出來。
handleResumeActivity()方法:

    final void handleResumeActivity(IBinder token, boolean clearHide,boolean isForward, boolean reallyResume) {
        //這個(gè)時(shí)候返弹, Activity.onResume()已經(jīng)調(diào)用了锈玉, 但是現(xiàn)在界面還是不可見的
        ActivityClientRecord r = performResumeActivity(token, clearHide);
        if (r != null) {
            final Activity a = r.activity;
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                //decor對(duì)用戶不可見
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    //被添加進(jìn)WindowManager了, 但是這個(gè)時(shí)候义起, 還是不可見的
                    wm.addView(decor, l);
                }
                if (!r.activity.mFinished && willBeVisible 
                        && r.activity.mDecor != null && !r.hideForNow) {
                    //在這里拉背, 執(zhí)行了重要的操作,使得DecorView可見
                    if (r.activity.mVisibleFromClient) {
                        r.activity.makeVisible();
                    }
                }
            }
        }
    }

當(dāng)我們執(zhí)行了Activity.makeVisible();之后,界面才對(duì)我們是可見的默终。

    void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            //將DecorView添加到WindowManager
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        //DecorView可見
        mDecor.setVisibility(View.VISIBLE);
    }

到此DecorView便可見椅棺,顯示在屏幕中。但是在這其中齐蔽,wm.addView(mDecor,getWindow().getAttributes()); 起到了重要的作用土陪, 因?yàn)槠鋬?nèi)部創(chuàng)建了一個(gè)ViewRootImpl對(duì)象, 負(fù)責(zé)繪制顯示各個(gè)子View肴熏。

總結(jié)

Activity就像個(gè)控制器, 不負(fù)責(zé)視圖部分顷窒。 Window像個(gè)承載器蛙吏, 裝著內(nèi)部視圖。 DecorView就是個(gè)頂層視圖鞋吉, 是所有View的最外層布局鸦做。ViewRoot像個(gè)連接器, 負(fù)責(zé)溝通谓着, 通過硬件的感知來通知視圖泼诱, 進(jìn)行用戶之間的交互。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末赊锚,一起剝皮案震驚了整個(gè)濱河市治筒,隨后出現(xiàn)的幾起案子屉栓,更是在濱河造成了極大的恐慌,老刑警劉巖耸袜,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件友多,死亡現(xiàn)場離奇詭異,居然都是意外死亡堤框,警方通過查閱死者的電腦和手機(jī)域滥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蜈抓,“玉大人启绰,你說我怎么就攤上這事」凳梗” “怎么了委可?”我有些...
    開封第一講書人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長格带。 經(jīng)常有香客問我撤缴,道長,這世上最難降的妖魔是什么叽唱? 我笑而不...
    開封第一講書人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任屈呕,我火速辦了婚禮,結(jié)果婚禮上棺亭,老公的妹妹穿的比我還像新娘虎眨。我一直安慰自己,他們只是感情好镶摘,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開白布嗽桩。 她就那樣靜靜地躺著,像睡著了一般凄敢。 火紅的嫁衣襯著肌膚如雪碌冶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,155評(píng)論 1 299
  • 那天涝缝,我揣著相機(jī)與錄音扑庞,去河邊找鬼。 笑死拒逮,一個(gè)胖子當(dāng)著我的面吹牛罐氨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播滩援,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼栅隐,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起租悄,我...
    開封第一講書人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤谨究,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后恰矩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體记盒,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年外傅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了纪吮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡萎胰,死狀恐怖碾盟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情技竟,我是刑警寧澤冰肴,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站榔组,受9級(jí)特大地震影響熙尉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜搓扯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一检痰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧锨推,春花似錦铅歼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至沾鳄,卻和暖如春慨飘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背译荞。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來泰國打工瓤的, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人磁椒。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像玫芦,于是被迫代替她去往敵國和親浆熔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容