Android应用程序窗口(Activity)的视图对象(View)的创建过程分析
文章轉(zhuǎn)載至CSDN社區(qū)羅升陽的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/8245546
從前文可知道,每一個Activity組件都有一個關(guān)聯(lián)的Window對象,用來描述一個應(yīng)用程序窗口。每一個應(yīng)用程序窗口內(nèi)部又包含有一個View對象,用來描述應(yīng)用程序窗口的視圖。應(yīng)用程序窗口視圖是真正用來實現(xiàn)UI內(nèi)容和布局的,也就是說,每一個Activity組件的UI內(nèi)容和布局都是通過與其所關(guān)聯(lián)的一個Window對象的內(nèi)部的一個View對象來實現(xiàn)的。在本文中,我們就詳細(xì)分析應(yīng)用程序窗口視圖的創(chuàng)建過程。
?? ? ? 在前面Android應(yīng)用程序窗口(Activity)實現(xiàn)框架簡要介紹和學(xué)習(xí)計劃一文中提到,應(yīng)用程序窗口內(nèi)部所包含的視圖對象的實際類型為DecorView。DecorView類繼承了View類,是作為容器(ViewGroup)來使用的,它的實現(xiàn)如圖1所示:
圖1 DecorView類的實現(xiàn)
?? ? ? ?這個圖的具體描述可以參考Android應(yīng)用程序窗口(Activity)實現(xiàn)框架簡要介紹和學(xué)習(xí)計劃一文中的圖5,這里不再詳述。
?? ? ? ?從前面Android應(yīng)用程序窗口(Activity)實現(xiàn)框架簡要介紹和學(xué)習(xí)計劃一文還可以知道,每一個應(yīng)用程序窗口的視圖對象都有一個關(guān)聯(lián)的ViewRoot對象,這些關(guān)聯(lián)關(guān)系是由窗口管理器來維護(hù)的,如圖2所示:
圖2 應(yīng)用程序窗口視圖與ViewRoot的關(guān)系圖
?? ? ? ?這個圖的具體描述可以參考Android應(yīng)用程序窗口(Activity)實現(xiàn)框架簡要介紹和學(xué)習(xí)計劃一文中的圖6,這里不再詳述。
?? ? ? ?簡單來說,ViewRoot相當(dāng)于是MVC模型中的Controller,它有以下職責(zé):
?? ? ? ?1. 負(fù)責(zé)為應(yīng)用程序窗口視圖創(chuàng)建Surface。
?? ? ? ?2. 配合WindowManagerService來管理系統(tǒng)的應(yīng)用程序窗口。
?? ? ? ?3. 負(fù)責(zé)管理、布局和渲染應(yīng)用程序窗口視圖的UI。
?? ? ? ?那么,應(yīng)用程序窗口的視圖對象及其所關(guān)聯(lián)的ViewRoot對象是什么時候開始創(chuàng)建的呢? 從前面Android應(yīng)用程序窗口(Activity)的窗口對象(Window)的創(chuàng)建過程分析一 文可以知道,Activity組件在啟動的時候,系統(tǒng)會為它創(chuàng)建窗口對象(Window),同時,系統(tǒng)也會為這個窗口對象創(chuàng)建視圖對象。另一方面,當(dāng) Activity組件被激活的時候,系統(tǒng)如果發(fā)現(xiàn)與它的應(yīng)用程序窗口視圖對象所關(guān)聯(lián)的ViewRoot對象還沒有創(chuàng)建,那么就會先創(chuàng)建這個 ViewRoot對象,以便接下來可以將它的UI渲染出來。
?? ? ? 從前面Android應(yīng)用程序啟動過程源代碼分析一 文可以知道,Activity組件在啟動的過程中,會調(diào)用ActivityThread類的成員函數(shù)handleLaunchActivity,用來創(chuàng)建 以及首次激活A(yù)ctivity組件,因此,接下來我們就從這個函數(shù)開始,具體分析應(yīng)用程序窗口的視圖對象及其所關(guān)聯(lián)的ViewRoot對象的創(chuàng)建過程,如 圖3所示:
圖3 應(yīng)用程序窗口視圖的創(chuàng)建過程
?? ? ? ?這個過程一共可以分為13個步驟,接下來我們就詳細(xì)分析每一個步驟。
?? ? ? ?Step 1. ActivityThread.handleLaunchActivity
?
[java] view plaincopy ?? ? ? ?這個函數(shù)定義在文件frameworks/base/core/java/android/app/ActivityThread.java文件中。
?? ? ? ?函數(shù)首先調(diào)用ActivityThread類的成員函數(shù)performLaunchActivity來創(chuàng)建要啟動的Activity組件。在創(chuàng)建 Activity組件的過程中,還會為該Activity組件創(chuàng)建窗口對象和視圖對象。Activity組件創(chuàng)建完成之后,就可以將它激活起來了,這是通 過調(diào)用ActivityThread類的成員函數(shù)handleResumeActivity來執(zhí)行的。
?? ? ? ?接下來,我們首先分析ActivityThread類的成員函數(shù)performLaunchActivity的實現(xiàn),以便可以了解應(yīng)用程序窗口視圖對象 的創(chuàng)建過程,接著再回過頭來繼續(xù)分析ActivityThread類的成員函數(shù)handleResumeActivity的實現(xiàn),以便可以了解與應(yīng)用程序 窗口視圖對象所關(guān)聯(lián)的ViewRoot對象的創(chuàng)建過程。
?? ? ? ?Step 2.?ActivityThread.performLaunchActivity
?? ? ? ?這個函數(shù)定義在文件frameworks/base/core/java/android/app/ActivityThread.java文件中。
?? ? ? ?這一步可以參考Android應(yīng)用程序窗口(Activity)的運行上下文環(huán)境(Context)的創(chuàng)建過程分析一文的Step 1,它主要就是創(chuàng)建一個Activity組件實例,并且調(diào)用這個Activity組件實例的成員函數(shù)onCreate來讓其執(zhí)行一些自定義的初始化工作。
?? ? ? ?Step 3.?Activity.onCreate
?? ? ? ?這個函數(shù)定義在文件frameworks/base/core/java/android/app/Activity.java中。
?? ? ? ?這一步可以參考Android應(yīng)用程序窗口(Activity)的運行上下文環(huán)境(Context)的創(chuàng)建過程分析一文的Step 10。我們在實現(xiàn)一個Activity組件的時候,也就是在實現(xiàn)一個Activity子類的時候,一般都會重寫成員函數(shù)onCreate,以便可以執(zhí)行一些自定義的初始化工作,其中就包含初始化UI的工作。例如,在前面在Ubuntu上為Android系統(tǒng)內(nèi)置Java應(yīng)用程序測試Application Frameworks層的硬件服務(wù)一文中,我們實現(xiàn)了一個名稱為Hello的Activity組件,用來測試硬件服務(wù),它的成員函數(shù)onCreate的樣子長得大概如下所示:
?
[java] view plaincopy?? ? ? 其中,調(diào)用從父類Activity繼承下來的成員函數(shù)setContentView就是用來創(chuàng)建應(yīng)用程序窗口視圖對象的。
?? ? ? 接下來,我們就繼續(xù)分析Activity類的成員函數(shù)setContentView的實現(xiàn)。
?? ? ? Step 4.?Activity.setContentView
?
[java] view plaincopy?? ? ? ?這個函數(shù)定義在文件frameworks/base/core/java/android/app/Activity.java中。
?? ? ? ?Activity類的成員函數(shù)setContentView首先調(diào)用另外一個成員函數(shù)getWindow來獲得成員變量mWindow所描述的一個窗口 對象,接著再調(diào)用這個窗口對象的成員函數(shù)setContentView來執(zhí)行創(chuàng)建應(yīng)用程序窗口視圖對象的工作。
?? ? ? ?從前面Android應(yīng)用程序窗口(Activity)的窗口對象(Window)的創(chuàng)建過程分析一文可以知道,Activity類的成員變量mWindow指向的是一個PhoneWindow對象,因此,接下來我們就繼續(xù)分析PhoneWindow類的成員函數(shù)setContentView的實現(xiàn)。
?? ? ? ?Step 5. PhoneWindow.setContentView
?
[java] view plaincopy?? ? ? ?這個函數(shù)定義在文件frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java中。
?? ? ? ?PhoneWindow類的成員變量mContentParent用來描述一個類型為DecorView的視圖對象,或者這個類型為DecorView 的視圖對象的一個子視圖對象,用作UI容器。當(dāng)它的值等于null的時候,就說明正在處理的應(yīng)用程序窗口的視圖對象還沒有創(chuàng)建。在這種情況下,就會調(diào)用成 員函數(shù)installDecor來創(chuàng)建應(yīng)用程序窗口視圖對象。否則的話,就說明是要重新設(shè)置應(yīng)用程序窗口的視圖。在重新設(shè)置之前,首先調(diào)用成員變量 mContentParent所描述的一個ViewGroup對象來移除原來的UI內(nèi)空。
?? ? ? ?由于我們是在Activity組件啟動的過程中創(chuàng)建應(yīng)用程序窗口視圖的,因此,我們就假設(shè)此時PhoneWindow類的成員變量 mContentParent的值等于null。接下來,函數(shù)就會調(diào)用成員函數(shù)installDecor來創(chuàng)建應(yīng)用程序窗口視圖對象,接著再通過調(diào)用 PhoneWindow類的成員變量mLayoutInflater所描述的一個LayoutInflater對象的成員函數(shù)inflate來將參數(shù) layoutResID所描述的一個UI布局設(shè)置到前面所創(chuàng)建的應(yīng)用程序窗口視圖中去,最后還會調(diào)用一個Callback接口的成員函數(shù) onContentChanged來通知對應(yīng)的Activity組件,它的視圖內(nèi)容發(fā)生改變了。從前面Android應(yīng)用程序窗口(Activity)的窗口對象(Window)的創(chuàng)建過程分析一文可以知道,Activity組件自己實現(xiàn)了這個Callback接口,并且將這個Callback接口設(shè)置到了與它所關(guān)聯(lián)的應(yīng)用程序窗口對象的內(nèi)部去,因此,前面實際調(diào)用的是Activity類的成員函數(shù)onContentChanged來發(fā)出一個視圖內(nèi)容變化通知。
?? ? ?接下來,我們就繼續(xù)分析PhoneWindow類的成員函數(shù)installDecor的實現(xiàn),以便可以繼續(xù)了解應(yīng)用程序窗口視圖對象的創(chuàng)建過程。
?? ? ?Step 6.?PhoneWindow.installDecor
?
[java] view plaincopy?? ? ? ?這個函數(shù)定義在文件frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java中。
?? ? ? ?由于我們是在Activity組件啟動的過程中創(chuàng)建應(yīng)用程序窗口視圖的,因此,我們同時假設(shè)此時PhoneWindow類的成員變量mDecor的值等 于null。這時候PhoneWindow類的成員函數(shù)installDecor就會調(diào)用另外一個成員函數(shù)generateDecor來創(chuàng)建一個 DecorView對象,并且保存在PhoneWindow類的成員變量mDecor中。
?? ? ? ?PhoneWindow類的成員函數(shù)installDecor接著再調(diào)用另外一個成員函數(shù)generateLayout來根據(jù)當(dāng)前應(yīng)用程序窗口的 Feature來加載對應(yīng)的窗口布局文件。這些布局文件保存在frameworks/base/core/res/res/layout目錄下,它們必須 包含有一個id值為“content”的布局控件。這個布局控件必須要從ViewGroup類繼承下來,用來作為窗口的UI容器。PhoneWindow 類的成員函數(shù)generateLayout執(zhí)行完成之后,就會這個id值為“content”的ViewGroup控件來給PhoneWindow類的成 員函數(shù)installDecor,后者再將其保存在成員變量mContentParent中。
?? ? ? PhoneWindow類的成員函數(shù)installDecor還會檢查前面加載的窗口布局文件是否包含有一個id值為“title”的TextView控 件。如果包含有的話,就會將它保存在PhoneWindow類的成員變量mTitleView中,用來描述當(dāng)前應(yīng)用程序窗口的標(biāo)題欄。但是,如果當(dāng)前應(yīng)用 程序窗口是沒有標(biāo)題欄的,即它的Feature位FEATURE_NO_TITLE的值等于1,那么PhoneWindow類的成員函數(shù) installDecor就需要將前面得到的標(biāo)題欄隱藏起來。注意,PhoneWindow類的成員變量mTitleView所描述的標(biāo)題欄有可能是包含 在一個id值為“title_container”的容器里面的,在這種情況下,就需要隱藏該標(biāo)題欄容器。另一方面,如果當(dāng)前應(yīng)用程序窗口是設(shè)置有標(biāo)題欄 的,那么PhoneWindow類的成員函數(shù)installDecor就會設(shè)置它的標(biāo)題欄文字。應(yīng)用程序窗口的標(biāo)題欄文字保存在PhoneWindow類 的成員變量mTitle中,我們可以調(diào)用PhoneWindow類的成員函數(shù)setTitle來設(shè)置。
?? ? ? 這一步執(zhí)行完成之后,應(yīng)用程序窗口視圖就創(chuàng)建完成了,回到前面的Step 1中,即ActivityThread類的成員函數(shù)handleLaunchActivity中,接下來就會調(diào)用ActivityThread類的另外一 個成員函數(shù)handleResumeActivity來激活正在啟動的Activity組件。由于在是第一次激活該Activity組件,因此,在激活之 前,還會為該Activity組件創(chuàng)建一個ViewRoot對象,并且與前面所創(chuàng)建的應(yīng)用程序窗口視圖關(guān)聯(lián)起來,以便后面可以通過該ViewRoot對象 來控制應(yīng)用程序窗口視圖的UI展現(xiàn)。
?? ? ? 接下來,我們就繼續(xù)分析ActivityThread類的成員函數(shù)handleResumeActivity的實現(xiàn)。
?? ? ? Step 7.?ActivityThread.handleResumeActivity
?
[java] view plaincopy?? ? ? ?這個函數(shù)定義在文件frameworks/base/core/java/android/app/ActivityThread.java中。
?? ? ? ?ActivityThread類的成員函數(shù)handleResumeActivity首先調(diào)用另外一個成員函數(shù) performResumeActivity來通知Activity組件,它要被激活了,即會導(dǎo)致Activity組件的成員函數(shù)onResume被調(diào) 用。ActivityThread類的成員函數(shù)performResumeActivity的返回值是一個ActivityClientRecord對象 r,這個ActivityClientRecord對象的成員變量activity描述的就是正在激活的Activity組件a。
?? ? ? ?ActivityThread類的成員函數(shù)handleResumeActivity接下來判斷正在激活的Activity組件接下來是否是可見的。如 果是可見的,那么變量willBeVisible的值就會等于true。Activity類的成員變量mStartedActivity用來描述一個 Activity組件是否正在啟動一個新的Activity組件,并且等待這個新的Activity組件的執(zhí)行結(jié)果。如果是的話,那么這個 Activity組件的成員變量mStartedActivity的值就會等于true,表示在新的Activity組件的執(zhí)行結(jié)果返回來之前,當(dāng)前 Activity組件要保持不可見的狀態(tài)。因此,當(dāng)Activity組件a的成員變量mStartedActivity的值等于true的時候,它接下來 就是不可見的,否則的話,就是可見的。
?? ? ? ?雖然說在Activity組件a的成員變量mStartedActivity的值等于true的情況下,它接下來的狀態(tài)要保持不可見的,但是有可能它所 啟動的Activity組件的UI不是全屏的。在這種情況下,Activity組件a的UI仍然是有部分可見的,這時候也要將變量 willBeVisible的值設(shè)置為true。因此,如果前面得到變量willBeVisible的值等于false,那么 ActivityThread類的成員函數(shù)handleResumeActivity接下來就會通過Binder進(jìn)程間通信機(jī)制來調(diào)用 ActivityManagerService服務(wù)的成員函數(shù)willActivityBeVisible來檢查位于Activity組件a上面的其它 Activity組件(包含了Activity組件a正在等待其執(zhí)行結(jié)果的Activity組件)是否是全屏的。如果不是,那么 ActivityManagerService服務(wù)的成員函數(shù)willActivityBeVisible的返回值就會等于true,表示接下來需要顯示 Activity組件a。
?? ? ? 前面得到的ActivityClientRecord對象r的成員變量window用來描述當(dāng)前正在激活的Activity組件a所關(guān)聯(lián)的應(yīng)用程序窗口對 象。當(dāng)它的值等于null的時候,就表示當(dāng)前正在激活的Activity組件a所關(guān)聯(lián)的應(yīng)用程序窗口對象還沒有關(guān)聯(lián)一個ViewRoot對象。進(jìn)一步地, 如果這個正在激活的Activity組件a還活著,并且接下來是可見的,即ActivityClientRecord對象r的成員變量mFinished 的值等于false,并且前面得到的變量willBeVisible的值等于true,那么這時候就說明需要為與Activity組件a所關(guān)聯(lián)的一個應(yīng)用 程序窗口視圖對象關(guān)聯(lián)的一個ViewRoot對象。
?? ? ? 將一個Activity組件的應(yīng)用程序窗口視圖對象與一個ViewRoot對象關(guān)聯(lián)是通過該Activity組件所使用的窗口管理器來執(zhí)行的。從前面Android應(yīng)用程序窗口(Activity)的窗口對象(Window)的創(chuàng)建過程分析一 文可以知道,一個Activity組件所使用的本地窗口管理器保存它的成員變量mWindowManager中,這可以通過Activity類的成員函數(shù) getWindowManager來獲得。在接下來的Step 10中,我們再分析Activity類的成員函數(shù)getWindowManager的實現(xiàn)。
?? ? ? 由于我們現(xiàn)在要給Activity組件a的應(yīng)用程序窗口視圖對象關(guān)聯(lián)一個ViewRoot對象,因此,我們就需要首先獲得這個應(yīng)用程序窗口視圖對象。從前 面的Step 6可以知道,一個Activity組件的應(yīng)用程序窗口視圖對象保存在與其所關(guān)聯(lián)的一個應(yīng)用程序窗口對象的內(nèi)部,因此,我們又要首先獲得這個應(yīng)用程序窗口對 象。與一個Activity組件所關(guān)聯(lián)的應(yīng)用程序窗口對象可以通過調(diào)用該Activity組件的成員函數(shù)getWindow來獲得。一旦獲得了這個應(yīng)用程 序窗口對象(類型為PhoneWindow)之后,我們就可以調(diào)用它的成員函數(shù)getDecorView來獲得它內(nèi)部的視圖對象。在接下來的Step 8和Step 9中,我們再分別分析Activity類的成員函數(shù)Activity類的成員函數(shù)getWindow和PhoneWindow類的成員函數(shù) getDecorView的實現(xiàn)。
?? ? ?在關(guān)聯(lián)應(yīng)用程序窗口視圖對象和ViewRoot對象的時候,還需要第三個參數(shù),即應(yīng)用程序窗口的布局參數(shù),這是一個類型為 WindowManager.LayoutParams的對象,可以通過調(diào)用應(yīng)用程序窗口的成員函數(shù)getAttributes來獲得。一切準(zhǔn)備就緒之 后,還要判斷最后一個條件是否成立,即當(dāng)前正在激活的Activity組件a在本地進(jìn)程中是否是可見的,即它的成員變量 mVisibleFromClient的值是否等于true。如果是可見的,那么最后就可以調(diào)用前面所獲得的一個本地窗口管理器wm(類型為 LocalWindowManager)的成員函數(shù)addView來執(zhí)行關(guān)聯(lián)應(yīng)用程序窗口視圖對象和ViewRoot對象的操作。
?? ? 接下來,我們就分別分析Activity類的成員函數(shù)getWindow、PhoneWindow類的成員函數(shù)getDecorView、ctivity 類的成員函數(shù)getWindowManager以及LocalWindowManager類的成員函數(shù)addView的實現(xiàn)。
?? ? Step 8.?Activity.getWindow
[java] view plaincopy?? ? ? ?這個函數(shù)定義在文件frameworks/base/core/java/android/app/Activity.java中。
?? ? ? ?從前面Android應(yīng)用程序窗口(Activity)的窗口對象(Window)的創(chuàng)建過程分析一文可以知道,Activity類的成員變量mWindow指向的是一個類型為PhoneWindow的窗口對象,因此,Activity類的成員函數(shù)getWindow返回給調(diào)用者的是一個PhoneWindow對象。
?? ? ? ?這一步執(zhí)完成之后,返回到前面的Step 7中,即ActivityThread類的成員函數(shù)handleResumeActivity中,接下來就會繼續(xù)調(diào)用前面所獲得的一個 PhoneWindow對象的成員函數(shù)getDecorView來獲得當(dāng)前正在激活的Activity組件所關(guān)聯(lián)的一個應(yīng)用程序窗口視圖對象。
?? ? ? ?Step 9.?PhoneWindow.getDecorView
[java] view plaincopy?? ? ? ?這個函數(shù)定義在文件frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java中。
?? ? ? ?PhoneWindow類的成員函數(shù)getDecorView首先判斷成員變量mDecor的值是否等于null。如果是的話,那么就說明當(dāng)前正在處理 的應(yīng)用程序窗口還沒有創(chuàng)建視圖對象。這時候就會調(diào)用另外一個成員函數(shù)installDecor來創(chuàng)建這個視圖對象。從前面的調(diào)用過程可以知道,當(dāng)前正在處 理的應(yīng)用程序窗口已經(jīng)創(chuàng)建過視圖對象,因此,這里的成員變量mDecor的值不等于null,PhoneWindow類的成員函數(shù) getDecorView直接將它返回給調(diào)用者。
?? ? ? ?這一步執(zhí)完成之后,返回到前面的Step 7中,即ActivityThread類的成員函數(shù)handleResumeActivity中,接下來就會繼續(xù)調(diào)用當(dāng)前正在激活的Activity組件 的成員函數(shù)getWindowManager來獲得一個本地窗口管理器。
?? ? ? ?Step 10.?Activity.getWindowManager
[java] view plaincopy?? ? ? ?這個函數(shù)定義在文件frameworks/base/core/java/android/app/Activity.java中。
?? ? ? ?從前面Android應(yīng)用程序窗口(Activity)的運行上下文環(huán)境(Context)的創(chuàng)建過程分析一文可以知道,Activity類的成員變量mWindowManager指向的一是類型為LocalWindowManager的本地窗口管理器,Activity類的成員函數(shù)getWindowManager直接將它返回給調(diào)用者。
?? ? ? ?這一步執(zhí)完成之后,返回到前面的Step 7中,即ActivityThread類的成員函數(shù)handleResumeActivity中,接下來就會繼續(xù)調(diào)用前面所獲得的一個 LocalWindowManager對象的成員函數(shù)addView來為當(dāng)前正在激活的Activity組件的應(yīng)用程序窗口視圖對象關(guān)聯(lián)一個 ViewRoot對象。
?? ? ? ?Step 11.?LocalWindowManager.addView
[java] view plaincopy?? ? ? ?這個函數(shù)定義在文件frameworks/base/core/java/android/view/Window.java中。
?? ? ? ?從前面Android應(yīng)用程序窗口(Activity)的窗口對象(Window)的創(chuàng)建過程分析一 文可以知道,LocalWindowManager類的成員變量mWindowManager指向的是一個WindowManagerImpl對象,因 此,LocalWindowManager類的成員函數(shù)addView接下來調(diào)用WindowManagerImpl類的成員函數(shù)addView來給參數(shù) view所描述的一個應(yīng)用程序窗口視圖對象關(guān)聯(lián)一個ViewRoot對象。
?? ? ? ?Step 12.?WindowManagerImpl.addView
[java] view plaincopy?? ? ? 這個函數(shù)定義在文件frameworks/base/core/java/android/view/WindowManagerImpl.java中。
?? ? ? 在WindowManagerImpl類中,兩個參數(shù)版本的成員函數(shù)addView是通過調(diào)用三個參數(shù)版本的成同函數(shù)addView來實現(xiàn)的,因此,我們接下來就主要分析三個參數(shù)版本的成員函數(shù)addView的實現(xiàn)。
?? ? ? 在分析WindowManagerImpl類的三個參數(shù)版本的成員函數(shù)addView的實現(xiàn)之前,我們首先了解一下WindowManagerImpl類 是如何關(guān)聯(lián)一個應(yīng)用程序窗口視圖對象(View對象)和一個ViewRoot對象的。一個View對象在與一個ViewRoot對象關(guān)聯(lián)的同時,還會關(guān)聯(lián) 一個WindowManager.LayoutParams對象,這個WindowManager.LayoutParams對象是用來描述應(yīng)用程序窗口 視圖的布局屬性的。
?? ? ??WindowManagerImpl類有三個成員變量mViews、mRoots和mParams,它們分別是類型為View、ViewRoot和 WindowManager.LayoutParams的數(shù)組。這三個數(shù)組的大小是始終保持相等的。這樣, 有關(guān)聯(lián)關(guān)系的View對象、ViewRoot對象和WindowManager.LayoutParams對象就會分別保存在數(shù)組mViews、 mRoots和mParams的相同位置上,也就是說,mViews[i]、mRoots[i]和mParams[i]所描述的View對象、 ViewRoot對象和WindowManager.LayoutParams對象是具有關(guān)聯(lián)關(guān)系的。因此,WindowManagerImpl類的三個 參數(shù)版本的成員函數(shù)addView在關(guān)聯(lián)一個View對象、一個ViewRoot對象和一個WindowManager.LayoutParams對象的 時候,只要分別將它們放在數(shù)組mViews、mRoots和mParams的相同位置上就可以了。
?? ? ? 理解了一個View對象、一個ViewRoot對象和一個WindowManager.LayoutParams對象是如何關(guān)聯(lián)之后,WindowManagerImpl類的三個參數(shù)版本的成員函數(shù)addView的實現(xiàn)就容易理解了。
?? ? ? 參數(shù)view和參數(shù)params描述的就是要關(guān)聯(lián)的View對象和WindowManager.LayoutParams對象。成員函數(shù)addView首 先調(diào)用另外一個成員函數(shù)findViewLocked來檢查參數(shù)view所描述的一個View對象是否已經(jīng)存在于數(shù)組中mViews中了。如果已經(jīng)存在的 話,那么就說明該View對象已經(jīng)關(guān)聯(lián)過ViewRoot對象以及WindowManager.LayoutParams對象了。在這種情況下,如果參數(shù) nest的值等于false,那么成員函數(shù)addView是不允許重復(fù)對參數(shù)view所描述的一個View對象進(jìn)行重新關(guān)聯(lián)的。另一方面,如果參數(shù) nest的值等于true,那么成員函數(shù)addView只是重新修改參數(shù)view所描述的一個View對象及其所關(guān)聯(lián)的一個ViewRoot對象內(nèi)部使用 的一個WindowManager.LayoutParams對象,即更新為參數(shù)params所描述的一個 WindowManager.LayoutParams對象,這是通過調(diào)用它們的成員函數(shù)setLayoutParams來實現(xiàn)的。
?? ? ? 如果參數(shù)view所描述的一個View對象還沒有被關(guān)聯(lián)過一個ViewRoot對象,那么成員函數(shù)addView就會創(chuàng)建一個ViewRoot對象,并且 將它與參數(shù)view和params分別描述的一個View對象和一個WindowManager.LayoutParams對象保存在數(shù)組mViews、 mRoots和mParams的相同位置上。注意,如果數(shù)組mViews、mRoots和mParams尚未創(chuàng)建,那么成員函數(shù)addView就會首先分 別為它們創(chuàng)建一個大小為1的數(shù)組,以便可以用來分別保存所要關(guān)聯(lián)的View對象、ViewRoot對象和 WindowManager.LayoutParams對象。另一方面,如果數(shù)組mViews、mRoots和mParams已經(jīng)創(chuàng)建,那么成員函數(shù) addView就需要分別將它們的大小增加1,以便可以在它們的末尾位置上分別保存所要關(guān)聯(lián)的View對象、ViewRoot對象和 WindowManager.LayoutParams對象。
?? ? ? 還有另外一個需要注意的地方是當(dāng)參數(shù)view描述的是一個子應(yīng)用程序窗口的視圖對象時,即WindowManager.LayoutParams對象 wparams的成員變量type的值大于等于WindowManager.LayoutParams.FIRST_SUB_WINDOW并且小于等于 WindowManager.LayoutParams.LAST_SUB_WINDOW時,那么成員函數(shù)addView還需要找到這個子視圖對象的父視 圖對象panelParentView,這是通過遍歷數(shù)組mRoots來查找的。首先,WindowManager.LayoutParams對象 wparams的成員變量token指向了一個類型為W的Binder本地對象的一個IBinder接口,用來描述參數(shù)view所描述的一個子應(yīng)用程序窗 口視圖對象所屬的父應(yīng)用程序窗口視圖對象。其次,每一個ViewRoot對象都通過其成員變量mWindow來保存一個類型為W的Binder本地對象, 因此,如果在數(shù)組mRoots中,存在一個ViewRoot對象,它的成員變量mWindow所描述的一個W對象的一個IBinder接口等于 WindowManager.LayoutParams對象wparams的成員變量token所描述的一個IBinder接口時,那么就說明與該 ViewRoot對象所關(guān)聯(lián)的View對象即為參數(shù)view的父應(yīng)用程序窗口視圖對象。
?? ? ? ?成員函數(shù)addView為參數(shù)view所描述的一個View對象和參數(shù)params所描述的一個WindowManager.LayoutParams 對象關(guān)聯(lián)好一個ViewRoot對象root之后,最后還會將這個View對view象和這個WindowManager.LayoutParams對 象,以及變量panelParentView所描述的一個父應(yīng)用程序窗視圖對象,保存在這個ViewRoot對象root的內(nèi)部去,這是通過調(diào)用這個 ViewRoot對象root的成員函數(shù)setView來實現(xiàn)的,因此,接下來我們就繼續(xù)分析ViewRoot類的成員函數(shù)setView的實現(xiàn)。
?? ? ? ?Step 13. ViewRoot.setView
[java] view plaincopy?? ? ? ?這個函數(shù)定義在文件frameworks/base/core/java/android/view/ViewRoot.java中。
?? ? ? ?參數(shù)view所描述的一個View對象會分別被保存在ViewRoot類的成員變量mView以及成員變量mAttachInfo所描述的一個 AttachInfo的成員變量mRootView中,而參數(shù)attrs所描述的一個WindowManager.LayoutParams對象的內(nèi)容會 被拷貝到ViewRoot類的成員變量mWindowAttributes中去。
?? ? ? 當(dāng)參數(shù)panelParentView的值不等于null的時候,就表示參數(shù)view描述的是一個子應(yīng)用程序窗口視圖對象。在這種情況下,參數(shù) panelParentView描述的就是一個父應(yīng)用程序窗口視圖對象。這時候我們就需要獲得用來描述這個父應(yīng)用程序窗口視圖對象的一個類型為W的 Binder本地對象的IBinder接口,以便可以保存在ViewRoot類的成員變量mAttachInfo所描述的一個AttachInfo的成員 變量mPanelParentWindowToken中去。這樣以后就可以知道ViewRoot類的成員變量mView所描述的一個子應(yīng)用程序窗口視圖所 屬的父應(yīng)用程序窗口視圖是什么了。注意,通過調(diào)用參數(shù)panelParentView的所描述的一個View對象的成員函數(shù) getApplicationWindowToken即可以獲得一個對應(yīng)的W對象的IBinder接口。
?? ? ? 上述操作執(zhí)行完成之后,ViewRoot類的成員函數(shù)setView就可以將成員變量mAdded的值設(shè)置為true了,表示當(dāng)前正在處理的一個 ViewRoot對象已經(jīng)關(guān)聯(lián)好一個View對象了。接下來,ViewRoot類的成員函數(shù)setView還需要執(zhí)行兩個操作:
?? ? ? 1. 調(diào)用ViewRoot類的另外一個成員函數(shù)requestLayout來請求對應(yīng)用程序窗口視圖的UI作第一次布局。
?? ? ? 2. 調(diào)用ViewRoot類的靜態(tài)成員變量sWindowSession所描述的一個類型為Session的Binder代理對象的成員函數(shù)add來請求 WindowManagerService增加一個WindowState對象,以便可以用來描述當(dāng)前正在處理的一個ViewRoot所關(guān)聯(lián)的一個應(yīng)用程 序窗口。
?? ? ? 至此,我們就分析完成Android應(yīng)用程序窗口視圖對象的創(chuàng)建過程了。在接下來的一篇文章中,我們將會繼續(xù)分析Android應(yīng)用程序窗口與 WindowManagerService服務(wù)的連接過程,即Android應(yīng)用程序窗口請求WindowManagerService為其增加一個 WindowState對象的過程,而在接下來的兩篇文章中,我們還會分析用來渲染Android應(yīng)用程序窗口的Surface的創(chuàng)建過程,以及 Android應(yīng)用程序窗口的渲染過程。通過這三個過程的分析,我們就可以看到上述第1點和第2點提到的兩個函數(shù)的執(zhí)行過程,敬請期待!
老羅的新浪微博:http://weibo.com/shengyangluo,歡迎關(guān)注!
總結(jié)
以上是生活随笔為你收集整理的Android应用程序窗口(Activity)的视图对象(View)的创建过程分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android服务之Service(其一
- 下一篇: Windows Server Backu