Resources資源的查找過程

以setContentView為列

1、Activity.setContentView

2、PhoneWindow.setContentView
.?LayoutInflater.inflate(layoutResID, mContentParent)

3、?LayoutInflater.inflate
getContext().getResources().getLayout(resource)返回XmlResourceParser,然后調用其成員函數inflate(parser, root, attachToRoot);

4、Resources.getLayout(resourceId)
? ? loadXmlResourceParser(resourceId, "layout");

5、Resources.loadXmlResourceParser
getValue(id, value, true);得到資源文件的名稱
loadXmlResourceParser(value.string.toString(), id, value.assetCookie, type);value 為TypeValue類型。

6、Resources.getValue
mAssets.getResourceValue(id, outValue, resolveRefs); mAssets為AssetManager類型

7、AssetManager.getResourceValue
loadResourceValue(ident, outValue, resolveRefs);返回Int,結果值保持在outValue中,sringBlock數組中的每一個StringBlock對象描述的都是當前應用程序使用的每一個資源索引表的資源項值字符串資源池,當AssetManager類的成員函數loadResourceValue的返回值block大于等于0的時候,實際上就表示參數ident所描述的資源項在當前應用程序使用的第block個資源索引表中。

C++層

8、AssetManager.loadResourceValue
?loadResourceValue()JNI方法,得到C++層的AssetsManager,調用其getResources方法,返回ResTable,資源表,?調用ResTable對象的成員函數getResource來獲得與參數ident所對應的資源項值及其配置信息,并且保存在類型為Res_value的變量value以及類型為ResTable_config的變量config中。
?如果參數resolve的值等于true,那么就繼續(xù)調用上述得到的ResTable對象的成員函數resolveReference來解析前面所得到的資源項值
調用函數copyValue將上述得到的資源項值及其配置信息拷貝到參數outValue所描述的一個Java層的TypedValue對象中去,返回調用者可以獲得與參數ident所對應的資源項內容。

9、?AssetManager.getResources
? ?getResTable(required):返回ResTable
當前應用程序所使用的資源包有兩個,其中一個是系統(tǒng)資源包,即/system/framework/framework-res.apk,另外一個就是自己的APK文件。這些APK文件的路徑都分別使用一個asset_path對象來描述,并且保存在AssetManager類的成員變量mAssetPaths中。

9.1、檢查資源包里面的resources.arsc文件已經提取出來,調用當前正在處理的AssetManager對象的成員變量mZipSet所指向的一個ZipSet對象的成員函數getZipResourceTableAsset就可以獲得一個對應的Asset對象,mZipSet所指向的一個ZipSet對象。
9.2、如果資源包里面的resources.arsc文件還沒有提取出來,那么就會調用當前正在處理的AssetManager對象的成員函數openNonAssetInPathLocked來將該resources.arsc文件提取出來。
9.3、Asset對象ass添加到變量rt所描述的一個ResTable對象中去,這是通過調用該ResTable對象的成員函數add來實現的

10、ResTable.getResource
參數resID描述的是要查找的資源的ID,ResTable類的成員函數getResource分別獲得它的Pakcage ID、Type ID以及Entry ID,保存在變量p、t以及e中。知道了Pakcage ID之后,就可以在ResTable類的成員變量mPackageGroups中找到對應的PakcageGroup

11、ResTable.getEntry

12、ResTable.resolveReference
ResTable類的成員函數resolveReference的實現其實很簡單,它就是對參數value所描述的一個資源項值進行解析,前提是這個資源項值是一個引用。一個資源項的值有可能是嵌套引用的,也就是可能是引用的引用,因此,ResTable類的成員函數resolveReference需要使用一個while循環(huán)來不斷地對參數value所描述的一個資源項值進行解析,直到最后一次解析出來的結果不是引用為止。但是,為了防止無限地解析下去,該while循環(huán)最多只允許執(zhí)行20次。每一次解析都是調用ResTable類的成員函數getResource來實現的,沿著調用路徑最終返回到前面的Step 5中,即Resources類的成員函數loadXmlResourceParser中,我們就可以得到參數id所描述的資源項的值了。

13、Resources.loadXmlResourceParser
Resources類最多可以緩存最近讀取的四個Xml資源文件的內容,讀取超過四個Xml資源文件之后 ,XmlBlock數組和資源ID數組就會被循環(huán)利用。
mCachedXmlBlockIds所指向的資源ID數組中檢查是否存在一個資源ID與參數id所描述的資源ID相等。如果存在的話,那么就會在成員變量mCachedXmlBlocks所指向的XmlBlock數組中找到一個對應的XmlBlock對象,并且調用這個XmlBlock對象的成員函數newParser來創(chuàng)建一個XmlResourceParser對象返回給調用者。
?如果在Resources類的成員變量mCachedXmlBlockIds所指向的資源ID數組找到對應的資源ID的話,那么Resources類的成員函數loadXmlResourceParser就會調用成員變量mAssets所指向的一個Java層的AssetManager對象的成員函數openXmlBlockAsset來打開參數file所指定的Xml資源文件,從而獲得一個XmlBlock對象block。這個XmlBlock對象block以及參數id所描述的資源ID同時也會被緩存在Resources類的成員變量mCachedXmlBlocks和mCachedXmlBlockIds所描述的XmlBlock數組和資源ID數組中。 最后,Resources類的成員函數loadXmlResourceParser就可以調用前面得到的XmlBlock對象block的成員函數newParser來創(chuàng)建一個XmlResourceParser對象返回給調用者。

14、AssetManager.openXmlBlockAsset

15、AssetManager.openXmlBlockAsset
? ? ? ? JNI方法,調用它的成員函數openNonAsset來打開參數fileName所指定的Xml資源文件,接下來就會創(chuàng)建一個ResXMLTree對象,并且將前面所打開的Xml資源文件的內容設置到該ResXMLTree對象中去,并且將該ResXMLTree對象的地址值返回給調用者。

16、AssetManager.openNonAsset
?AssetManager類的成員函數openNonAsset通過參數cookie知道了當前要打開的文件是位于哪個APK文件之后,接著就繼續(xù)調用另外一個成員函數openNonAssetInPathLocked來打開該文件

17、?AssetManager.openNonAssetInPathLocked
如果參數ap描述的文件路徑是一個目錄,那么它就會直接將參數fileName所指向的文件名稱附加到該目錄后面去,然后直接調用另外一個成員函數openAssetFromFileLocked來打開參數fileName所指向的文件。如果打開失敗,那么就再假定參數ap描述的文件路徑是一個以“.gz“為后綴的壓縮包,然后再次調用成員函數openAssetFromFileLocked來打開參數fileName所指向的文件。 如果參數ap描述的文件路徑是一個普通文件,那么就意味著參數ap描述的是一個壓縮文件,因此,它就會先調用成員函數getZipFileLocked來打開該壓縮文件,然后再調用成員函數openAssetFromZipLocked來在該壓縮包將參數fileName所指向的文件提取出來。Android應用程序的資源一般都是打包在一個APK文件里面的,而APK文件就是一個Zip格式的壓縮包,因此,AssetManager類的成員函數openNonAssetInPathLocked一般就是按照第二種方式來打開參數fileName所指向的文件。

18、AssetManager.openAssetFromZipLocked
就是從參數pZipFile所描述的壓縮包中將參數entryName所描述的文件提取出來,并且根據提取出來的內容保存在一個Asset對象中返回給調用者。

19、?XmlBlock.newParser
?XmlBlock類的成員函數mNative指向的是C++層的一個ResXMLTree對象,調用JNI方法nativeCreateParseState,用來在C++層創(chuàng)建一個ResXMLParser對象,最后再將該C++層的ResXMLParser對象封裝成Java層的一個Parser對象中,并且將該Parser對象返回給調用者

20、?LayoutInflater.inflate
?LayoutInflater類的成員函數inflate主要負責處理前面所打開的Xml資源文件的根節(jié)點,然后再調用另外一個成員函數rInflate來處理根節(jié)點的子節(jié)點。每一個節(jié)點都表示一個UI控件,這個UI控件是通過調用LayoutInflater類的成員函數createViewFromTag來創(chuàng)建的。

21、LayoutInflater.createViewFromTag
LayoutInflater類的成員函數createViewFromTag接下來檢查成員變量mFactory的值是否不等于null。如果不等于null的話,那么它就會指向一個Factory對象,該Factory對象描述的是一個UI控件創(chuàng)建工廠,專門用來負責創(chuàng)建UI控件。
如果LayoutInflater類的成員變量mFactory的值等于null,那么LayoutInflater類的成員函數createViewFromTag就會調用成員函數onCreateView或者createView來創(chuàng)建由參數name所指定的UI控件,取決于參數name是否包含了一個“.”字符。注意,如果參數name是否包含了一個“.”字符,那么就說明當前所創(chuàng)建的UI控件是一個用戶自定義的UI控件,也就是不是Android提供的標準控件。

22、?PhoneLayoutInflater.onCreateView
? PhoneLayoutInflater類的成員函數onCreateView只負責創(chuàng)建兩類標準的UI控件,一種是屬于android.widget包的,另一種是屬于android.webkit包的,其中,優(yōu)先創(chuàng)建android.widget包的UI控件。
?如果參數name所描述的UI控件既不屬于android.widget包的,也不屬于android.webkit包的,那么?PhoneLayoutInflater類的成員函數onCreateView就會將創(chuàng)建UI控件的操作交給父類來處理,即通過調用父類的成員函數onCreateView來創(chuàng)建。
如果參數name所描述的UI控件是屬于android.widget包或者android.webkit包的,那么PhoneLayoutInflater類的成員函數onCreateView就會直接調用父類LayoutInflater的成員函數createView來創(chuàng)建參數name所描述的UI控件,因此,接下來我們就繼續(xù)分析LayoutInflater類的成員函數createView的實現。

23、?LayoutInflater.createView

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

相關閱讀更多精彩內容

友情鏈接更多精彩內容