作者: ztelur
原文地址:
http://blog.csdn.net/u012422440/article/details/51173387
最近一直在研究View
的繪制相關(guān)的機(jī)制,發(fā)現(xiàn)需要補(bǔ)充一下Android View Architecture的相關(guān)知識(shí)姐刁,所以就特地研究了一下這方面的代碼聂使,寫成本篇文章。
為了節(jié)約你的時(shí)間弃理,本篇文章內(nèi)容大致如下:
-
Activity
屎蜓,DecorView
炬转,PhoneWindow
和ViewRoot
的作用和相關(guān)關(guān)系。
Android View Architecture
先來幾張圖驻啤,大致展現(xiàn)一下Android 視圖架構(gòu)的大概:
Activity和Window
眾所周知骑冗,Activity并不負(fù)責(zé)視圖控制捍靠,它只是控制生命周期和處理事件榨婆,真正控制視圖的是Window
。一個(gè)Activity包含了一個(gè)Window谊迄,Window才是真正代表一個(gè)窗口烟央,也就是說Activity可以沒有Window,那就相當(dāng)于是Service了粮呢。在ActivityThread
中也有控制Service
的相關(guān)函數(shù)或許正好印證了這一點(diǎn)。
Activity
和Window
的第一次邂逅是在ActivityThread
調(diào)用Activity
的attach()
函數(shù)時(shí)豪硅。
//[window]:通過PolicyManager創(chuàng)建window,實(shí)現(xiàn)callback函數(shù),所以,當(dāng)window接收到
//外界狀態(tài)改變時(shí),會(huì)調(diào)用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);
//當(dāng)window接收系統(tǒng)發(fā)送給它的IO輸入事件時(shí),例如鍵盤和觸摸屏事件,就可以轉(zhuǎn)發(fā)給相應(yīng)的Activity
mWindow.setCallback(this);
.....
//設(shè)置本地窗口管理器
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
.....
}
在attach()
中懒浮,新建一個(gè)Window
實(shí)例作為自己的成員變量识藤,它的類型為PhoneWindow
痴昧,這是抽象類Window的一個(gè)子類。然后設(shè)置mWindow
的WindowManager
秧骑。
Window,Activity和DecorView
DecorView
是FrameLayout
的子類扣囊,它可以被認(rèn)為是Android視圖樹的根節(jié)點(diǎn)視圖绒疗。DecorView
作為頂級(jí)View,一般情況下它內(nèi)部包含一個(gè)豎直方向的LinearLayout
吓蘑,在這個(gè)LinearLayout
里面有上下兩個(gè)部分(具體情況和Android版本及主體有關(guān))磨镶,上面的是標(biāo)題欄,下面的是內(nèi)容欄伟叛。在Activity中通過setContentView
所設(shè)置的布局文件其實(shí)就是被加到內(nèi)容欄之中的脐嫂,而內(nèi)容欄的id是content,在代碼中可以通過ViewGroup content = (ViewGroup)findViewById(R.android.id.content)
來得到content
對(duì)應(yīng)的layout
侥蒙。
Window
中有幾個(gè)視圖相關(guān)的比較重要的成員變量如下所示:
-
mDecor
:DecorView
的實(shí)例鞭衩,標(biāo)示Window
內(nèi)部的頂級(jí)視圖; -
mContentParent
:setContetView
所設(shè)置的布局文件就加到這個(gè)視圖中恒水; -
mContentRoot
:是DecorView
的唯一子視圖饲齐,內(nèi)部包含mContentParent
,標(biāo)題欄和狀態(tài)欄御雕。
Activity中不僅持有一個(gè)Window
實(shí)例滥搭,還有一個(gè)類型為View
的mDecor
實(shí)例瑟匆。這個(gè)實(shí)例和Window
中的mDecor
實(shí)例有什么關(guān)系呢?它又是什么時(shí)候被創(chuàng)建的呢疾嗅?
二者其實(shí)指向同一個(gè)對(duì)象冕象,這個(gè)對(duì)象是在Activity
調(diào)用setContentView
時(shí)創(chuàng)建的。我們都知道Activity
的setContentView
實(shí)際上是調(diào)用了Window
的setContentView
方法论悴。
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) { //[window]如何沒有DecorView,那么就新建一個(gè)
installDecor(); //[window]
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
....
//[window]第二步,將layout添加到mContentParent
mLayoutInflater.inflate(layoutResID, mContentParent);
.....
}
代碼很清楚的顯示了布局文件的視圖是添加到mContentParent
中膀估,而且Window
通過installDecor
來新建DecorView
耻讽。
//[window]創(chuàng)建一個(gè)decorView
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor(); //直接new出一個(gè)DecorView返回
....
}
if (mContentParent == null) {
//[window] 這一步也是很重要的.
mContentParent = generateLayout(mDecor); //mContentParent是setContentVIew的關(guān)鍵啊
.....
}
....
}
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
.......
//[window] 根據(jù)不同的style生成不同的decorview啊
View in = mLayoutInflater.inflate(layoutResource, null);
// 加入到deco中,所以應(yīng)該是其第一個(gè)child
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in; //給DecorView的第一個(gè)child是mContentView
// 這是獲得所謂的content
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
}
.....
return contentParent;
}
從上述的代碼中齐饮,我們可以清楚的看到mDecor
、mContentParent
和mContentRoot
的關(guān)系握恳。
那么捺僻,Activity
中的mDecor
是何時(shí)被賦值的?我們?nèi)绾未_定它和Widnow
中的mDecor
指向同一個(gè)對(duì)象呢拔稳?我們可以查看ActivityThread
的handleResumeActivity
函數(shù)锹雏,它負(fù)責(zé)處理Activity
的resume
階段。在這個(gè)函數(shù)中轻绞,Android直接將Window
中的DecorView
實(shí)例賦值給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
對(duì)應(yīng)ViewRootImpl
類,它是連接WindowManagerService
和DecorView
的紐帶懒叛,View的三大流程(測量(measure)戏挡,布局(layout)褐墅,繪制(draw))均通過ViewRoot
來完成洪己。ViewRoot
并不屬于View樹的一份子。從源碼實(shí)現(xiàn)上來看逝钥,它既非View的子類拱镐,也非View的父類沃琅,但是,它實(shí)現(xiàn)了ViewParent
接口晌柬,這讓它可以作為View
的名義上的父視圖。RootView
繼承了Handler
類澈歉,可以接收事件并分發(fā)屿衅,Android的所有觸屏事件、按鍵事件凯砍、界面刷新等事件都是通過ViewRoot
進(jìn)行分發(fā)的拴竹。ViewRoot可以被理解為“View樹的管理者”——它有一個(gè)mView
成員變量栓拜,它指向的對(duì)象和上文中Window
和Activity
的mDecor
指向的對(duì)象是同一個(gè)對(duì)象。
我們來先看一下ViewRoot
的創(chuàng)建過程挑势。由于ViewRoot
作為WindowMangerService
和DecorView
的紐帶啦鸣,只有在WindowManager
將持有DecorView
的Window
添加進(jìn)窗口管理器才創(chuàng)建诫给。我們可以查看WindowMangerGlobal
中的addView
函數(shù)。對(duì)WindowManager
不太熟悉的同學(xué)可以參考《Window和WindowManager解析》
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
// 創(chuàng)建ViewRootImpl,然后將下述對(duì)象添加到列表中
....
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
....
try {
// 添加啦!!!!!!!!這是通過ViewRootImpl的setView來完成凫碌,這個(gè)View就是DecorView實(shí)例
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
....
}
....
}
那么盛险,Window
是什么時(shí)候被添加到WindowManager
中的呢勋又?我們回到ActivityThread
的handleResumeActivity
函數(shù)。我們都知道Activity
的resume
階段就是要顯示到屏幕上的階段鹤啡,在Activity
也就是DecorView
將要顯示到屏幕時(shí)挺邀,系統(tǒng)才會(huì)調(diào)用addView
方法或链。
我們?cè)?code>handleResumeActivity函數(shù)中找到了下面一段代碼练般,它調(diào)用了Activity
的makeVisible()
函數(shù)。
// 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ā)現(xiàn)狂丝,正式在makeVisible
函數(shù)中几颜,系統(tǒng)進(jìn)行了Window
的添加讯屈。