導語 Android系統(tǒng)內部是在什么時候創(chuàng)建AssetManager先较,又是如何創(chuàng)建的携冤? AssetManager與Resource什么關系? Android插件化為什么可以通過重寫addAssetPath方法訪問插件資源闲勺? 本文參考老羅的文章曾棕,并生成自己的見解,若有錯誤之處霉翔,懇請指正睁蕾。 詳見:http://blog.csdn.net/luoshengyang/article/details/8791064
1、AssetManager與Resources
Android中的資源可以分為兩類:
第一類資源,不對應文件的子眶,如string資源瀑凝;
第二類資源,對應文件的臭杰,如layout資源粤咪。
-
第一類資源只需通過資源ID查找到資源名稱即可,第二類資源需根據資源名稱打開相應文件渴杆。 通過下面這副圖片簡單的分析下資源查找的過程:
如上圖所示寥枝,Resources根據資源ID查找資源,AssetManager根據資源名稱查找資源磁奖。若Resources查找資源時的資源ID對應的資源是文件囊拜,那先根據資源ID查找到資源文件名稱(在resources.arsc中查找到),再通過AssetManager打開相應文件比搭。 看下Resources與AssetManager的成員變量:
ContextImpl提供接口getResources可以獲得指向當前APK資源的Resources對象冠跷。
Resouces類中的mAssets是AssetManager對象,用來訪問程序的非編譯文件(第二類資源)身诺;mSystem是Resources對象蜜托,用來訪問系統(tǒng)資源包,系統(tǒng)資源包存在/system/framework/framework-res.apk霉赡。
AssetManager(Java)中的sSystem是AssetManager對象橄务,用來打開系統(tǒng)資源包文件;mObject是一個int地址穴亏,保存了對應C++層的AssetManager對象地址蜂挪。
AssetManager(C++)中的mAssetPaths是資源目錄,mResources為資源表迫肖,mConfig保存了設備的本地配置信息锅劝。
2攒驰、Activity啟動
調用startActivity啟動指定Activity蟆湖,最后會調用ApplicationThread的scheduleLaunchActivity方法。流程如下所示:
scheduleLaunchActivity方法通過H發(fā)送消息H.LAUNCH_ACTIVITY
H的消息處理中玻粪,調用ActivityThread的getPackageInfoNoCheck創(chuàng)建LoadedAPK對象隅津,調用handleLaunchActivity啟動Activity。
創(chuàng)建的LoadedAPK對象指定資源路徑為當前APK劲室。
handleLaunchActivity啟動Activity伦仍,先調用makeApplication創(chuàng)建Application對象,并未Application關聯(lián)ContextImpl(baseContext)很洋。
再為啟動的Activity關聯(lián)ComtextImpl(baseContext)充蓝。 該過程如下圖所示,其中AMS(ActivityManagerService)負責Activity聲明周期和Activity堆棧的管理,而ApplicationThread是App進程與AMS通信的Binder谓苟。ApplicationThread通過H(是一個Handler)實現(xiàn)與主線程ActivityThread的通信官脓,進而管理者Activity的聲明周期。
在上過程中涝焙,首先會為創(chuàng)建的Application關聯(lián)AppContext卑笨,即為Application關聯(lián)ContextImpl;再為啟動的Activity關聯(lián)BaseContext仑撞,同樣為ContextImpl赤兴。該過程都會調用ContextImpl的構造方法,實現(xiàn)ContextImpl的創(chuàng)建隧哮,并調用init方法實現(xiàn)Resources桶良、AssetManager的創(chuàng)建,那接著看ContextImpl的init過程沮翔。
(插一句艺普,Application繼承ContextWrapper,ContextWrapper繼承Context鉴竭;Activity繼承ContextThemeWrapper歧譬,ContextThemeWrapper繼承ContextWrapper。Application與Activity的基類Context是抽象類搏存,其具有成員變量mBaseContext瑰步,采用代理模式,真正的操作都有mBaseContext實現(xiàn)璧眠。而真正實現(xiàn)Context方法的類只有ContextImpl缩焦,因此在創(chuàng)建Activity或Application都需要為其關聯(lián)一個ContextImpl對象)
3、ActivityManager創(chuàng)建
接著上一步的ContextImpl的創(chuàng)建责静,先看下時序圖:
該過程的主要實現(xiàn)了:
過程還是比較多的袁滥,挑幾步好好看下。
3.1灾螃、第1步:init
該步中調用LoadeAPk的getResources(..)方法獲得Resources對象题翻,LoadedAPK在上面Activity啟動流程中進行創(chuàng)建(LoadedAPK保存了當前APK信息)。
3.2腰鬼、第3步:getTopLevelResources
在第2步調用LoadedAPK的getResources方法中嵌赠,它會調用ActivityThread的getTopLevelResources方法。該方法的主要內容有:
如上所示熄赡,該方法主要完成以下功能:
從Map緩存中取Resources對象姜挺,有直接返回,沒有下一步彼硫。
創(chuàng)建AssetManager炊豪,并接著添加系統(tǒng)資源文件路徑到資源目錄(mAssetPaths)中凌箕。
添加訪問的APK文件(當前APK文件)路徑到資源目錄(mAssetPaths)中。
創(chuàng)建Resources對象词渤,并返回陌知。
3.2、第7步:addAssetPath
在前面的分析中可知掖肋,通過調用addAssetPath方法將資源文件路徑添加到資源目錄中仆葡,實現(xiàn)資源的加載。該方法的主要內容有:
由上可知志笼,addAssetPath主要是將資源文件路徑添加到資源目錄mAssetPaths中沿盅,并且判斷添加的apk文件是否位于/system/framework/下,如果是則會在/Vendor/overlay/frame/目錄下查找是否有同名的apk文件纫溃,有的話則用該apk文件覆蓋原有apk腰涧。(該機制一般用于廠商自定義資源覆蓋系統(tǒng)資源)。
4紊浩、總結
由上分析可知窖铡,ContextImpl創(chuàng)建過程中,會調研getResources()獲得Resources對象坊谁,而getResources最后調用getTopLevelResources方法费彼。getTopLevelResources方法首先從緩存中拿Resources對象,沒有拿到則先創(chuàng)建AssetManager對象口芍,并通過AssetManager的addAssetPath實現(xiàn)系統(tǒng)資源文件箍铲、當前APK資源文件的加載,然后再創(chuàng)建Resources對象返回鬓椭。
5颠猴、后記
在andorid插件化機制中,關于如何訪問插件資源⌒∪荆現(xiàn)在的一般做法是通過反射拿到AssetManager的addAssetPath方法翘瓮,將插件資源路徑添加到資源目錄mAssetPath中,然后在創(chuàng)建Resources對象裤翩。
而另外一種方案則來自于DroidPlugin機制资盅,該插件化機制采用Hook思想,當啟動插件Activity時岛都,先替換啟動的插件Activity為代理Activity律姨,再在H的消息處理中替換回插件Activity振峻。而在H的消息處理中替換回插件Activity時臼疫,首先通過插件Acitivity信息創(chuàng)建LoadedAPK對象(指定資源路徑為插件路徑),再調用LoadedAPK的makeApplication創(chuàng)建插件Application扣孟。而關于makeApplication調用上面已經講過了烫堤,它會調用ContextImpl創(chuàng)建過程實現(xiàn)插件Resources的創(chuàng)建以及資源加載。