Android視圖架構詳解

作者: ztelur
聯(lián)系方式:segmentfaultcsdngithub

本文僅供個人學習,不用于任何形式商業(yè)目的,轉載請注明原作者、文章來源,鏈接,版權歸原文作者所有。

?最近一直在研究View的繪制相關的機制,發(fā)現需要補充一下Android View Architecture的相關知識,所以就特地研究了一下這方面的代碼,寫成本篇文章
?為了節(jié)約你的時間,本篇文章內容大致如下:

  • ActivityDecorViewPhoneWindowViewRoot的作用和相關關系

Android View Architecture

?先來幾張圖,大致展現一下Android 視圖架構的大概。

View Architecutre類圖
視圖關系圖.png
View樹狀結構圖.png

Activity和Window

?總所周知,Activity并不負責視圖控制,它只是控制生命周期和處理事件,真正控制視圖的是Window。一個Activity包含了一個Window,Window才是真正代表一個窗口,也就是說Activity可以沒有Window,那就相當于是Service了。在ActivityThread中也有控制Service的相關函數或許正好印證了這一點。
?ActivityWindow的第一次邂逅是在ActivityThread調用Activityattach()函數時。

//[window]:通過PolicyManager創(chuàng)建window,實現callback函數,所以,當window接收到
//外界狀態(tài)改變時,會調用activity的方法,
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) {
    ....
    mWindow = PolicyManager.makeNewWindow(this);
    //當window接收系統(tǒng)發(fā)送給它的IO輸入事件時,例如鍵盤和觸摸屏事件,就可以轉發(fā)給相應的Activity
    mWindow.setCallback(this);
    .....
    //設置本地窗口管理器
    mWindow.setWindowManager(
            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
            mToken, mComponent.flattenToString(),
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    .....
}

?在attach()中,新建一個Window實例作為自己的成員變量,它的類型為PhoneWindow,這是抽象類Window的一個子類。然后設置mWindowWindowManager

Window,Activity和DecorView

?DecorViewFrameLayout的子類,它可以被認為是Android視圖樹的根節(jié)點視圖。DecorView作為頂級View,一般情況下它內部包含一個豎直方向的LinearLayout,在這個LinearLayout里面有上下兩個部分(具體情況和Android版本及主體有關),上面的是標題欄,下面的是內容欄。在Activity中通過setContentView所設置的布局文件其實就是被加到內容欄之中的,而內容欄的id是content,在代碼中可以通過ViewGroup content = (ViewGroup)findViewById(R.android.id.content)來得到content對應的layout。
?Window中有幾個視圖相關的比較重要的成員變量如下所示:

  • mDecor:DecorView的實例,標示Window內部的頂級視圖
  • mContentParent:setContetView所設置的布局文件就加到這個視圖中
  • mContentRoot:是DecorView的唯一子視圖,內部包含mContentParent,標題欄和狀態(tài)欄。

?Activity中不僅持有一個Window實例,還有一個類型為ViewmDecor實例。這個實例和Window中的mDecor實例有什么關系呢?它又是什么時候被創(chuàng)建的呢?
?二者其實指向同一個對象,這個對象是在Activity調用setContentView時創(chuàng)建的。我們都知道ActivitysetContentView實際上是調用了WindowsetContentView方法。

@Override
public void setContentView(int layoutResID) {
    if (mContentParent == null) { //[window]如何沒有DecorView,那么就新建一個
        installDecor(); //[window]
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }
    ....
    //[window]第二步,將layout添加到mContentParent
    mLayoutInflater.inflate(layoutResID, mContentParent);
    .....
}

?代碼很清楚的顯示了布局文件的視圖是添加到mContentParent中,而且Window通過installDecor來新建DecorView

//[window]創(chuàng)建一個decorView
private void installDecor() {
    if (mDecor == null) {
        mDecor = generateDecor(); //直接new出一個DecorView返回
        ....
    }
    if (mContentParent == null) {
        //[window] 這一步也是很重要的.
        mContentParent = generateLayout(mDecor); //mContentParent是setContentVIew的關鍵啊
        .....
    }
    ....
}
protected ViewGroup generateLayout(DecorView decor) {
    // Apply data from current theme.
    .......
    //[window] 根據不同的style生成不同的decorview啊
    View in = mLayoutInflater.inflate(layoutResource, null);
    // 加入到deco中,所以應該是其第一個child
    decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    mContentRoot = (ViewGroup) in; //給DecorView的第一個child是mContentView
    // 這是獲得所謂的content 
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    }
    .....
    return contentParent;
}

?從上述的代碼中,我們可以清楚的看到mDecormContentParentmContentRoot的關系。
?那么,Activity中的mDecor是何時被賦值的?我們如何確定它和Widnow中的mDecor指向同一個對象呢?我們可以查看ActivityThreadhandleResumeActivity函數,它負責處理Activityresume階段。在這個函數中,Android直接將Window中的DecorView實例賦值給Activity

final Activity a = r.activity;
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;

Window,DecorView 和 ViewRoot

?ViewRoot對應ViewRootImpl類,它是連接WindowManagerServiceDecorView的紐帶,View的三大流程(測量(measure),布局(layout),繪制(draw))均通過ViewRoot來完成。ViewRoot并不屬于View樹的一份子。從源碼實現上來看,它既非View的子類,也非View的父類,但是,它實現了ViewParent接口,這讓它可以作為View的名義上的父視圖。RootView繼承了Handler類,可以接收事件并分發(fā),Android的所有觸屏事件、按鍵事件、界面刷新等事件都是通過ViewRoot進行分發(fā)的。ViewRoot可以被理解為“View樹的管理者”——它有一個mView成員變量,它指向的對象和上文中WindowActivitymDecor指向的對象是同一個對象

?我們來先看一下ViewRoot的創(chuàng)建過程。由于ViewRoot作為WindowMangerServiceDecorView的紐帶,只有在WindowManager將持有DecorViewWindow添加進窗口管理器才創(chuàng)建。我們可以查看WindowMangerGlobal中的addView函數。對WindowManager不太熟悉的同學可以參考《Window和WindowManager解析》

public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
        // 創(chuàng)建ViewRootImpl,然后將下述對象添加到列表中
    ....
    root = new ViewRootImpl(view.getContext(), display);

    view.setLayoutParams(wparams);

    mViews.add(view);
    mRoots.add(root);
    mParams.add(wparams);
    ....
    try {
        // 添加啦!!!!!!!!這是通過ViewRootImpl的setView來完成,這個View就是DecorView實例
        root.setView(view, wparams, panelParentView);
    } catch (RuntimeException e) {
      ....
    }
    ....
}

?那么,Window是什么時候被添加到WindowManager中的呢?我們回到ActivityThreadhandleResumeActivity函數。我們都知道Activity的resume階段就是要顯示到屏幕上的階段,在Activity也就是DecorView將要顯示到屏幕時,系統(tǒng)才會調用addView方法。
?我們在handleResumeActivity函數中找到了下面一段代碼,它調用了ActivitymakeVisible()函數。

// ActivityThread
r.activity.makeVisible();

//Activity
    //[windows] DecorView正式添加并顯示
    void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }

?我們通過源代碼發(fā)現,正式在makeVisible函數中,系統(tǒng)進行了Window的添加。

引用
http://wiki.jikexueyuan.com/project/deep-android-v1/surface.html
http://blog.csdn.net/guxiao1201/article/details/41744107
http://forlan.iteye.com/blog/2269381

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,197評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 98,415評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,104評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,884評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,647評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,130評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,208評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,366評論 0 288
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,737評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,478評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,174評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,586評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,827評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,608評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,914評論 2 372

推薦閱讀更多精彩內容