初探Android中Window與DecorView

Android中View可以說是最為重要的幾個地方之一,包括事件分發(fā),測量,繪制等等,都是非常常見的情況。那么我們要想好好掌握這些知識,就得深入了解Andorid整個View從開始到完成所經(jīng)歷的一系列工作。本文分析的源代碼均來自Android API 24。

Activity和Window

在Android中,Activity并不負責視圖控制,它只是控制生命周期和處理事件,真正控制視圖的是Window。一個Activity包含了一個Window,Window才是真正代表一個窗口,也就是說Activity如果沒有Window,那就和Service沒多大差別了。
Window第一次出現(xiàn)在Activity中是在ActivityThread調(diào)用Activity的attach()方法時,通過PolicyManager創(chuàng)建window,實現(xiàn)callback方法,所以,當window接收到
外界狀態(tài)改變時,會調(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 = new PhoneWindow(this, window); //在這里直接new一個Window
mWindow.setCallback(this);//設置回調(diào)接口,當window接收系統(tǒng)發(fā)送給它的例如鍵盤和觸摸屏事件,就可以通知Activity,Activity做出相應的處理
.....
//設置窗口管理器
mWindow.setWindowManager(
     (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
        mToken, mComponent.flattenToString(),
        (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
.....
}

在attach()中,Activity新建一個PhoneWindow實例作為成員變量,這是Window的唯一一個子類。然后設置mWindow的WindowManager。

DecorView

首先簡單介紹一下DecorView,這里引用任玉剛大神的《Android開發(fā)藝術探索》書中的介紹:它是一個頂級View,內(nèi)部會包含一個豎直方向的LinearLayout,這個LinearLayout有上下兩部分,分為titlebar和contentParent兩個子元素,contentParent的id是content,而我們自定義的Activity的布局就是contentParent里面的一個子元素。View層的所有事件都要先經(jīng)過DecorView后才傳遞給我們的View。那么DecorView是何時出現(xiàn)的呢??又是如何和window產(chǎn)生聯(lián)系呢??


Activity類的成員變量

首先,如上圖我們可以看到Activity中包含了Window,DecorView,WindowManager等成員變量,那么我們就從Activity的創(chuàng)建過程中去逐步尋找它們。
首先在Activity中的onCreate方法中我們都會寫一句setContentView的方法調(diào)用,將我們自定義的Activity布局傳入。那么我們跟進該方法去看一下具體實現(xiàn)。



通過getWindow來獲取成員變量mWindow。
image.png

然后調(diào)用window的setContentView()方法。該方法來進行我們Activity的布局設置。然后又接著調(diào)用了initWindowDecorActionBar()方法來進行對ActionBar的新建和初始化。我們暫時不管該方法。
mWindow類型是Window,是一個接口,而在安卓中實現(xiàn)Window接口的實現(xiàn)類只有PhoneWindow,所以進入PhoneWindow查看setContentView()方法的具體實現(xiàn)。先上源代碼:



首先會在上圖標記1處判斷mContentParent是否為空,如果為空,會執(zhí)行installDecor()方法,那么mContentParent是什么呢?
通過定義可以看出,mContentParent是一個ViewGroup類型的變量,它是Activity的一個成員變量。我們所寫的setContetView()所設置的布局文件就加到這個視圖中。根據(jù)官方解釋它可能是mDecor自身,也可能是mDecor的子View。
繼續(xù)回到setContentView中看代碼,如果mContentParent為空,那么會執(zhí)行installDecor()方法,跟進這個方法,顧名思義,新建DecorView。下面給出源代碼:

在installDecor方法中標注1處,如果mDecor為空(mDecor是Window持有的一個成員變量,指的就是DecorView),那么在 generateDecor()中去實例化新建DecorView,我們繼續(xù)跟進generateDecor()查看源代碼:

在這里前面一段代碼都是初始化所需要的context,最后返回一個實例化的DecorView()。在DecorView構造方法中進行初始化工作。


image.png

上圖是DecorView的一些初始化工作,就不再展開,有興趣的同志們可以研究。
返回到installDecor()方法中繼續(xù)往下,到標記處2,會判斷mContentParent是否為空,如果為空,通過 generateLayout()來將我們具體的布局加載到DecorView中。跟入 generateLayout()方法查看源代碼(由于方法中代碼過長不便于截圖,我貼出關鍵代碼):

...//代碼前面都是在獲取主題相關 

//在這個方法里將mContentParent的布局轉換并添加到DecorView中
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);//具體代碼在下一個代碼框中

ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);// contentParent就是root的父布局。
return contentParent;

final View root = inflater.inflate(layoutResource, null);//將mContentParent布局轉換為View
mDecor.addView(root,new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));//將mContentParent加入到DecorView中
mContentRoot = (ViewGroup) root;//mContentRoot就是mContentParent視圖

至此generateLayout()方法分析完畢,返回contentParent變量,賦值給mContentParent成員變量。

隨后上述步驟也是installDecor()方法的主要代碼,因此繼續(xù)返回到setContentView方法繼續(xù)查看(把setContentView的圖繼續(xù)貼下來避免返回去翻圖):

圖中標注處2的代碼,則是將我們的自定義布局加入到mContentParent(也就是id為R.id.content)布局中。

最后標記處3則是通過回調(diào)來通知ActivityContent已經(jīng)發(fā)生了變化,由Activity來做出相應的處理。


DecorView和Window

至此已經(jīng)setContentView已經(jīng)完成,DecorView也已經(jīng)新建,我們自定義布局也加入到mContentParent布局中,但是此時mDecorView還沒有被WindowManager正式添加到Window中。因此Window此時還無法接受外界的信息,也無法提供具體的功能。
所以在activity中的onResume()方法中調(diào)用了makeVisible()方法來進行添加,同時也是把Window加入到WindowManager中

void makeVisible() {  
  if (!mWindowAdded) {     
   ViewManager wm = getWindowManager(); //獲得WindowManager(WindowManager extends ViewManager)
   wm.addView(mDecor, getWindow().getAttributes());   //將DecorView加入到Window中
   mWindowAdded = true;   
  }   
  mDecor.setVisibility(View.VISIBLE);
}

至此整個decorView的加載過程和Activity的Window創(chuàng)建過程已經(jīng)完成,Decor將會顯示出來呈現(xiàn)在手機屏幕上.


ps:由于是大二萌新第一次寫東西,所以很多地方還很模糊,希望前輩們多多指點錯誤,也希望能繼續(xù)加油記錄自己的點點滴滴
再ps:本文參考和學習了任玉剛大神的《Android開發(fā)藝術探索》和陳育大神的文章(http://www.itdecent.cn/p/687010ccad66)

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容