Activity視圖對象的創(chuàng)建過程

我們在開發(fā)android程序城看,如果要寫一個(gè)界面摄杂,很自然就想到幾個(gè)步驟:創(chuàng)建一個(gè)Activity類杈湾,寫一個(gè)xml布局矢门,在回調(diào)函數(shù)onCreate()通過setContentView()把xml布局文件設(shè)置進(jìn)Activity膝迎,然后一個(gè)簡單的界面就出來了粥帚,相當(dāng)簡單!可是限次,在這個(gè)過程中android系統(tǒng)到底經(jīng)歷了什么呢芒涡,作為一名應(yīng)用開發(fā)工程師,我們大部分只關(guān)心上層的實(shí)現(xiàn)卖漫,并不清楚android系統(tǒng)層面的實(shí)現(xiàn)原理费尽,這篇文章我將分析與Activity視圖相關(guān)的對象Window、WindowManager和View的之間關(guān)系和它們創(chuàng)建過程羊始。

Window的初始化過程


Activity有一個(gè)類型為Window的成員變量旱幼,它在Activity成員函數(shù)attach()被初始化,附上源碼:

public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback {
  ......
  private Window mWindow;
  ......

  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) {
        attachBaseContext(context);
        mFragments.attachHost(null /*parent*/);
        mWindow = new PhoneWindow(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
        ......    
        mWindow.setWindowManager(null, mToken, mComponent.flattenToString());    
        ......    
    } 

整個(gè)代碼的調(diào)用流程可以用下面的時(shí)序圖來概括:

圖1

attach()函數(shù)主要做了以下幾件事情:

  • 初始化一個(gè)PhoneWindow對象并賦給mWindow突委,由此可知PhoneWindow是Window的子類柏卤,兩者的關(guān)系可以參考下面的圖2;
  • 調(diào)用PhoneWindow的成員函數(shù)setCallback()設(shè)置Activity的回調(diào),該回調(diào)用來通知鍵盤和觸摸回調(diào)匀油;
  • 調(diào)用setSoftInputMode()設(shè)置描述窗口的軟鍵盤輸入?yún)^(qū)域的顯示模式缘缚;
  • 為PhoneWindow創(chuàng)建一個(gè)WindowManagerImpl對象并保存在成員變量mWindowManager中,WindowManagerImpl用來處理后面會(huì)講到的View對象敌蚜;

Activity的所有視圖相關(guān)的屬性和布局對象都是通過PhoneWindow來維護(hù)的桥滨,我通過一個(gè)類圖來體現(xiàn)Window和PhoneWindow的關(guān)系:

圖2

View的創(chuàng)建過程

通過下面的時(shí)序圖我們可以知道整View的創(chuàng)建過程


圖3

其中ActivityThread是創(chuàng)建Activity的一個(gè)處理類,通過這個(gè)類來處理Activity的生命周期钝侠,這個(gè)流程不是這篇文章討論的范疇该园,我們只要知道系統(tǒng)經(jīng)過一系列復(fù)雜的過程最終通過performLaunchActivity調(diào)用了Activity的onCreate函數(shù),而我們要開發(fā)應(yīng)用程序一般都會(huì)在onCreate函數(shù)調(diào)用setContentView來設(shè)置布局帅韧,最終調(diào)用了PhoneWindow的setContentView()里初,我們來看源碼:

public class PhoneWindow extends Window implements MenuBuilder.Callback {
    // This is the top-level view of the window, containing the window decor.
    private DecorView mDecor;

    // This is the view in which the window contents are placed. It is either
    // mDecor itself, or a child of mDecor where the contents go.
    private ViewGroup mContentParent;

    private ViewGroup mContentRoot;
    ......
    @Override
    public void setContentView(int layoutResID) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }
    .......
    private void installDecor() {  
        if (mDecor == null) {  
            mDecor = generateDecor();  
            ......  
        }  
        if (mContentParent == null) {  
            mContentParent = generateLayout(mDecor);  
  
            mTitleView = (TextView)findViewById(com.android.internal.R.id.title);  
            if (mTitleView != null) {  
                if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {  
                    View titleContainer = findViewById(com.android.internal.R.id.title_container);  
                    if (titleContainer != null) {  
                        titleContainer.setVisibility(View.GONE);  
                    } else {  
                        mTitleView.setVisibility(View.GONE);  
                    }  
                    if (mContentParent instanceof FrameLayout) {  
                        ((FrameLayout)mContentParent).setForeground(null);  
                    }  
                } else {  
                    mTitleView.setText(mTitle);  
                }  
            }  
        }  
    }  
    ......

通過代碼分析,PhoneWindow通過以下幾個(gè)步驟來創(chuàng)建View:

  • 創(chuàng)建一個(gè)DecorView對象mDecor忽舟,通過以上的圖2我們知道DecorView繼承自FrameLayout双妨,它是Activity的頂級視圖淮阐,Activity顯示的所有界面內(nèi)容都放在里面;
  • 根據(jù)Activity的Feature加載不同的布局文件刁品,這些布局文件放在frameworks/base/core/res/res/layout目錄下泣特,它們必須包含有一個(gè)id值為“content”的布局控件,加載完后解析成一個(gè)布局對象mContentRoot挑随,接著把mContentRoot當(dāng)作子布局添加到mDecor中状您;
  • 從mContentRoot獲取到id為“content”的子布局mContentParent,將我們自定義的整個(gè)布局添加到mContentParent中兜挨;

最終可以通過下面的圖表示activity的view布局結(jié)構(gòu):

圖4

這樣我們就知道和Activity相關(guān)的Window和View的創(chuàng)建過程了膏孟,接下來,如圖3的時(shí)序圖拌汇,ActivityThread會(huì)將創(chuàng)建好的視圖對象Window和布局對象通過addView()方法最終傳遞到WindowManagerGlobal柒桑,后者就會(huì)將每個(gè)Activity對應(yīng)的Window和View關(guān)聯(lián)起來,最終傳遞到WindowManagerService噪舀,后者會(huì)統(tǒng)一協(xié)調(diào)整個(gè)系統(tǒng)的Activity視圖布局魁淳,根據(jù)需要將需要渲染的activity布局內(nèi)容交給另一個(gè)叫SurfaceFlinger的服務(wù)去渲染出來,這個(gè)時(shí)候我們就能在屏幕上看到具體的布局圖形了与倡。關(guān)于WindowManagerService和SurfaceFlinger涉及了另外兩塊更加復(fù)雜的內(nèi)容界逛,此篇文章不作詳細(xì)分析。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末纺座,一起剝皮案震驚了整個(gè)濱河市仇奶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌比驻,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件岛抄,死亡現(xiàn)場離奇詭異别惦,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)夫椭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進(jìn)店門掸掸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蹭秋,你說我怎么就攤上這事扰付。” “怎么了仁讨?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵羽莺,是天一觀的道長。 經(jīng)常有香客問我没讲,道長应役,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任显沈,我火速辦了婚禮刁卜,結(jié)果婚禮上志电,老公的妹妹穿的比我還像新娘。我一直安慰自己蛔趴,他們只是感情好挑辆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著孝情,像睡著了一般鱼蝉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上咧叭,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天蚀乔,我揣著相機(jī)與錄音,去河邊找鬼菲茬。 笑死吉挣,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的婉弹。 我是一名探鬼主播睬魂,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼镀赌!你這毒婦竟也來了氯哮?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤商佛,失蹤者是張志新(化名)和其女友劉穎喉钢,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體良姆,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肠虽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了玛追。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片税课。...
    茶點(diǎn)故事閱讀 40,144評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖痊剖,靈堂內(nèi)的尸體忽然破棺而出韩玩,到底是詐尸還是另有隱情,我是刑警寧澤陆馁,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布找颓,位于F島的核電站,受9級特大地震影響氮惯,放射性物質(zhì)發(fā)生泄漏叮雳。R本人自食惡果不足惜想暗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望帘不。 院中可真熱鬧说莫,春花似錦、人聲如沸寞焙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽捣郊。三九已至辽狈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間呛牲,已是汗流浹背刮萌。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留娘扩,地道東北人着茸。 一個(gè)月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像琐旁,于是被迫代替她去往敵國和親涮阔。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評論 2 355

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