日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > Android >内容正文

Android

Android应用程序资源管理器(Asset Manager)的创建过程分析

發(fā)布時(shí)間:2024/1/1 Android 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android应用程序资源管理器(Asset Manager)的创建过程分析 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

? ? ? ? 在前面一篇文章中,我們分析了Android應(yīng)用程序資源的編譯和打包過(guò)程,最終得到的應(yīng)用程序資源就與應(yīng)用程序代碼一起打包在一個(gè)APK文件中。Android應(yīng)用程序在運(yùn)行的過(guò)程中,是通過(guò)一個(gè)稱(chēng)為AssetManager的資源管理器來(lái)讀取打包在APK文件里面的資源文件的。在本文中,我們就將詳細(xì)分析Android應(yīng)用程序資源管理器的創(chuàng)建以及初始化過(guò)程,為接下來(lái)的一篇文章分析應(yīng)用程序資源的讀取過(guò)程打下基礎(chǔ)。

《Android系統(tǒng)源代碼情景分析》一書(shū)正在進(jìn)擊的程序員網(wǎng)(http://0xcc0xcd.com)中連載,點(diǎn)擊進(jìn)入!

? ? ? ? 從前面Android應(yīng)用程序窗口(Activity)的運(yùn)行上下文環(huán)境(Context)的創(chuàng)建過(guò)程分析一文可以知道,應(yīng)用程序的每一個(gè)Activity組件都關(guān)聯(lián)有一個(gè)ContextImpl對(duì)象,這個(gè)ContextImpl對(duì)象就是用來(lái)描述Activity組件的運(yùn)行上下文環(huán)境的。Activity組件是從Context類(lèi)繼承下來(lái)的,而ContextImpl同樣是從Context類(lèi)繼承下來(lái)的。我們?cè)贏ctivity組件調(diào)用的大部分成員函數(shù)都是轉(zhuǎn)發(fā)給與它所關(guān)聯(lián)的一個(gè)ContextImpl對(duì)象的對(duì)應(yīng)的成員函數(shù)來(lái)處理的,其中就包括用來(lái)訪問(wèn)應(yīng)用程序資源的兩個(gè)成員函數(shù)getResources和getAssets。

? ? ? ??ContextImpl類(lèi)的成員函數(shù)getResources返回的是一個(gè)Resources對(duì)象,有了這個(gè)Resources對(duì)象之后,我們就可以通過(guò)資源ID來(lái)訪問(wèn)那些被編譯過(guò)的應(yīng)用程序資源了。ContextImpl類(lèi)的成員函數(shù)getAssets返回的是一個(gè)AssetManager對(duì)象,有了這個(gè)AssetManager對(duì)象之后,我們就可以通過(guò)文件名來(lái)訪問(wèn)那些被編譯過(guò)或者沒(méi)有被編譯過(guò)的應(yīng)用程序資源文件了。事實(shí)上,Resources類(lèi)也是通過(guò)AssetManager類(lèi)來(lái)訪問(wèn)那些被編譯過(guò)的應(yīng)用程序資源文件的,不過(guò)在訪問(wèn)之前,它會(huì)先根據(jù)資源ID查找得到對(duì)應(yīng)的資源文件名。

? ? ? ? 我們知道,在Android系統(tǒng)中,一個(gè)進(jìn)程是可以同時(shí)加載多個(gè)應(yīng)用程序的,也就是可以同時(shí)加載多個(gè)APK文件。每一個(gè)APK文件在進(jìn)程中都對(duì)應(yīng)有一個(gè)全局的Resourses對(duì)象以及一個(gè)全局的AssetManager對(duì)象。其中,這個(gè)全局的Resourses對(duì)象保存在一個(gè)對(duì)應(yīng)的ContextImpl對(duì)象的成員變量mResources中,而這個(gè)全局的AssetManager對(duì)象保存在這個(gè)全局的Resourses對(duì)象的成員變量mAssets中。上述ContextImpl、Resourses和AssetManager的關(guān)系如圖1所示:


圖1 ContextImpl、Resources和AssetManager的關(guān)系圖

? ? ? ? Resources類(lèi)有一個(gè)成員函數(shù)getAssets,通過(guò)它就可以獲得保存在Resources類(lèi)的成員變量mAssets中的AssetManager,例如,ContextImpl類(lèi)的成員函數(shù)getAssets就是通過(guò)調(diào)用其成員變量mResources所指向的一個(gè)Resources對(duì)象的成員函數(shù)getAssets來(lái)獲得一個(gè)可以用來(lái)訪問(wèn)應(yīng)用程序的非編譯資源文件的AssetManager。

? ? ? ? 我們知道,Android應(yīng)用程序除了要訪問(wèn)自己的資源之外,還需要訪問(wèn)系統(tǒng)的資源。系統(tǒng)的資源打包在/system/framework/framework-res.apk文件中,它在應(yīng)用程序進(jìn)程中是通過(guò)一個(gè)單獨(dú)的Resources對(duì)象和一個(gè)單獨(dú)的AssetManager對(duì)象來(lái)管理的。這個(gè)單獨(dú)的Resources對(duì)象就保存在Resources類(lèi)的靜態(tài)成員變量mSystem中,我們可以通過(guò)Resources類(lèi)的靜態(tài)成員函數(shù)getSystem就可以獲得這個(gè)Resources對(duì)象,而這個(gè)單獨(dú)的AssetManager對(duì)象就保存在AssetManager類(lèi)的靜態(tài)成員變量sSystem中,我們可以通過(guò)AssetManager類(lèi)的靜態(tài)成員函數(shù)getSystem同樣可以獲得這個(gè)AssetManager對(duì)象。

? ? ? ? AssetManager類(lèi)除了在Java層有一個(gè)實(shí)現(xiàn)之外,在 C++層也有一個(gè)對(duì)應(yīng)的實(shí)現(xiàn),而Java層的AssetManager類(lèi)的功能就是通過(guò)C++層的AssetManager類(lèi)來(lái)實(shí)現(xiàn)的。Java層的每一個(gè)AssetManager對(duì)象都有一個(gè)類(lèi)型為int的成員變量mObject,它保存的便是在C++層對(duì)應(yīng)的AssetManager對(duì)象的地址,因此,通過(guò)這個(gè)成員變量就可以將Java層的AssetManager對(duì)象與C++層的AssetManager對(duì)象關(guān)聯(lián)起來(lái)。

? ? ? ? C++層的AssetManager類(lèi)有三個(gè)重要的成員變量mAssetPaths、mResources和mConfig。其中,mAssetPaths保存的是資源存放目錄,mResources指向的是一個(gè)資源索引表,而mConfig保存的是設(shè)備的本地配置信息,例如屏幕密度和大小、國(guó)家地區(qū)和語(yǔ)言等等配置信息。有了這三個(gè)成員變量之后,C++層的AssetManager類(lèi)就可以訪問(wèn)應(yīng)用程序的資源了。

? ? ? ? 從前面Android應(yīng)用程序啟動(dòng)過(guò)程源代碼分析一文可以知道,每一個(gè)Activity組件在進(jìn)程的加載過(guò)程中,都會(huì)創(chuàng)建一個(gè)對(duì)應(yīng)的ContextImpl,并且調(diào)用這個(gè)ContextImpl對(duì)象的成員函數(shù)init來(lái)執(zhí)行初始化Activity組件運(yùn)行上下文環(huán)境的工作,其中就包括創(chuàng)建用來(lái)訪問(wèn)應(yīng)用程序資源的Resources對(duì)象和AssetManager對(duì)象的工作,接下來(lái),我們就從ContextImpl類(lèi)的成員函數(shù)init開(kāi)始分析Resources對(duì)象和AssetManager對(duì)象的創(chuàng)建以及初始化過(guò)程,如圖2所示:

圖2 應(yīng)用程序資源管理器的創(chuàng)建和初始化過(guò)程

? ? ? ? 這個(gè)過(guò)程可以分為14個(gè)步驟,接下來(lái)我們就詳細(xì)分析每一個(gè)步驟。

? ? ? ? Step 1. ContextImpl.init

class ContextImpl extends Context {....../*package*/ LoadedApk mPackageInfo;private Resources mResources;......final void init(LoadedApk packageInfo,IBinder activityToken, ActivityThread mainThread) {init(packageInfo, activityToken, mainThread, null);}final void init(LoadedApk packageInfo,IBinder activityToken, ActivityThread mainThread,Resources container) {mPackageInfo = packageInfo;mResources = mPackageInfo.getResources(mainThread);......}...... }

? ? ? ? 這個(gè)函數(shù)定義在文件frameworks/base/core/java/android/app/ContextImpl.java中。

? ? ? ? 參數(shù)packageInfo指向的是一個(gè)LoadedApk對(duì)象,這個(gè)LoadedApk對(duì)象描述的是當(dāng)前正在啟動(dòng)的Activity組所屬的Apk。三個(gè)參數(shù)版本的成員函數(shù)init調(diào)用了四個(gè)參數(shù)版本的成員函數(shù)init來(lái)初始化當(dāng)前正在啟動(dòng)的Activity組件的運(yùn)行上下文環(huán)境。其中,用來(lái)訪問(wèn)應(yīng)用程序資源的Resources對(duì)象是通過(guò)調(diào)用參數(shù)packageInfo所指向的是一個(gè)LoadedApk對(duì)象的成員函數(shù)getResources來(lái)創(chuàng)建的。這個(gè)Resources對(duì)象創(chuàng)建完成之后,就會(huì)保存在ContextImpl類(lèi)的成員變量mResources中。

? ? ? ? 接下來(lái),我們就繼續(xù)分析LoadedApk類(lèi)的成員函數(shù)getResources的實(shí)現(xiàn)。

? ? ? ? Step 2.?LoadedApk.getResources

final class LoadedApk {......private final String mResDir;......Resources mResources;......public Resources getResources(ActivityThread mainThread) {if (mResources == null) {mResources = mainThread.getTopLevelResources(mResDir, this);}return mResources;}...... }? ? ? ? 這個(gè)函數(shù)定義在文件frameworks/base/core/java/android/app/LoadedApk.java中。

? ? ? ? 參數(shù)mainThread指向了一個(gè)ActivityThread對(duì)象,這個(gè)ActivityThread對(duì)象描述的是當(dāng)前正在運(yùn)行的應(yīng)用程序進(jìn)程。

? ? ? ? LoadedApk類(lèi)的成員函數(shù)getResources首先檢查其成員變量mResources的值是否等于null。如果不等于的話,那么就會(huì)將它所指向的是一個(gè)Resources對(duì)象返回給調(diào)用者,否則的話,就會(huì)調(diào)用參數(shù)mainThread所指向的一個(gè)ActivityThread對(duì)象的成員函數(shù)getTopLevelResources來(lái)獲得這個(gè)Resources對(duì)象,然后再返回給調(diào)用者。

? ? ? ? 在調(diào)用ActivityThread類(lèi)的成員函數(shù)getTopLevelResources來(lái)獲得一個(gè)Resources對(duì)象的時(shí)候,需要指定要獲取的Resources對(duì)象所對(duì)應(yīng)的Apk文件路徑,這個(gè)Apk文件路徑就保存在LoadedApk類(lèi)的成員變量mResDir中。例如,假設(shè)我們要獲取的Resources對(duì)象是用來(lái)訪問(wèn)系統(tǒng)自帶的音樂(lè)播放器的資源的,那么對(duì)應(yīng)的Apk文件路徑就為/system/app/Music.apk。

? ? ? ? 接下來(lái),我們就繼續(xù)分析ActivityThread類(lèi)的成員函數(shù)getTopLevelResources的實(shí)現(xiàn)。

? ? ? ? Step 3.?ActivityThread.getTopLevelResources

public final class ActivityThread {......final HashMap<ResourcesKey, WeakReference<Resources> > mActiveResources= new HashMap<ResourcesKey, WeakReference<Resources> >();......Resources getTopLevelResources(String resDir, CompatibilityInfo compInfo) {ResourcesKey key = new ResourcesKey(resDir, compInfo.applicationScale);Resources r;synchronized (mPackages) {......WeakReference<Resources> wr = mActiveResources.get(key);r = wr != null ? wr.get() : null;......if (r != null && r.getAssets().isUpToDate()) {......return r;}}......AssetManager assets = new AssetManager();if (assets.addAssetPath(resDir) == 0) {return null;}......r = new Resources(assets, metrics, getConfiguration(), compInfo);......synchronized (mPackages) {WeakReference<Resources> wr = mActiveResources.get(key);Resources existing = wr != null ? wr.get() : null;if (existing != null && existing.getAssets().isUpToDate()) {// Someone else already created the resources while we were// unlocked; go ahead and use theirs.r.getAssets().close();return existing;}// XXX need to remove entries when weak references go awaymActiveResources.put(key, new WeakReference<Resources>(r));return r;}}...... }? ? ? ? 這個(gè)函數(shù)定義在文件frameworks/base/core/java/android/app/ActivityThread.java中。

? ? ? ??ActivityThread類(lèi)的成員變量mActiveResources指向的是一個(gè)HashMap。這個(gè)HashMap用來(lái)維護(hù)在當(dāng)前應(yīng)用程序進(jìn)程中加載的每一個(gè)Apk文件及其對(duì)應(yīng)的Resources對(duì)象的對(duì)應(yīng)關(guān)系。也就是說(shuō),給定一個(gè)Apk文件路徑,ActivityThread類(lèi)的成員函數(shù)getTopLevelResources可以在成員變量mActiveResources中檢查是否存在一個(gè)對(duì)應(yīng)的Resources對(duì)象。如果不存在,那么就會(huì)新建一個(gè),并且保存在ActivityThread類(lèi)的成員變量mActiveResources中。

? ? ? ? 參數(shù)resDir即為要獲取其對(duì)應(yīng)的Resources對(duì)象的Apk文件路徑,ActivityThread類(lèi)的成員函數(shù)getTopLevelResources首先根據(jù)它來(lái)創(chuàng)建一個(gè)ResourcesKey對(duì)象,然后再以這個(gè)ResourcesKey對(duì)象在ActivityThread類(lèi)的成員變量mActiveResources中檢查是否存在一個(gè)Resources對(duì)象。如果存在,并且這個(gè)Resources對(duì)象里面包含的資源文件沒(méi)有過(guò)時(shí),即調(diào)用這個(gè)Resources對(duì)象的成員函數(shù)getAssets所獲得一個(gè)AssetManager對(duì)象的成員函數(shù)isUpToDate的返回值等于true,那么ActivityThread類(lèi)的成員函數(shù)getTopLevelResources就可以將該Resources對(duì)象返回給調(diào)用者了。

? ? ? ? 如果不存在與參數(shù)resDir對(duì)應(yīng)的Resources對(duì)象,或者存在這個(gè)Resources對(duì)象,但是存在的這個(gè)Resources對(duì)象是過(guò)時(shí)的,那么ActivityThread類(lèi)的成員函數(shù)getTopLevelResources就會(huì)新創(chuàng)建一個(gè)AssetManager對(duì)象,并且調(diào)用這個(gè)新創(chuàng)建的AssetManager對(duì)象的成員函數(shù)addAssetPath來(lái)將參數(shù)resDir所描述的Apk文件路徑作為它的資源目錄。

? ? ? ? 創(chuàng)建了一個(gè)新的AssetManager對(duì)象,ActivityThread類(lèi)的成員函數(shù)getTopLevelResources還需要這個(gè)AssetManager對(duì)象來(lái)創(chuàng)建一個(gè)新的Resources對(duì)象。這個(gè)新創(chuàng)建的Resources對(duì)象需要以前面所創(chuàng)建的ResourcesKey對(duì)象為鍵值緩存在ActivityThread類(lèi)的成員變量mActiveResources所描述的一個(gè)HashMap中,以便以后可以獲取回來(lái)使用。不過(guò),這個(gè)新創(chuàng)建的Resources對(duì)象在緩存到ActivityThread類(lèi)的成員變量mActiveResources所描述的一個(gè)HashMap去之前,需要再次檢查該HashMap是否已經(jīng)存在一個(gè)對(duì)應(yīng)的Resources對(duì)象了,這是因?yàn)楫?dāng)前線程在創(chuàng)建新的AssetManager對(duì)象和Resources對(duì)象的過(guò)程中,可能有其它線程搶先一步創(chuàng)建了與參數(shù)resDir對(duì)應(yīng)的Resources對(duì)象,并且將該Resources對(duì)象保存到該HashMap中去了。

? ? ? ? 如果沒(méi)有其它線程搶先創(chuàng)建一個(gè)與參數(shù)resDir對(duì)應(yīng)的Resources對(duì)象,或者其它線程搶先創(chuàng)建出來(lái)的Resources對(duì)象是過(guò)時(shí)的,那么ActivityThread類(lèi)的成員函數(shù)getTopLevelResources就會(huì)將前面創(chuàng)建的Resources對(duì)象緩存到成員變量mActiveResources所描述的一個(gè)HashMap中去,并且將前面創(chuàng)建的Resources對(duì)象返回給調(diào)用者,否則擴(kuò)知,就會(huì)將其它線程搶先創(chuàng)建的Resources對(duì)象返回給調(diào)用者。

? ? ? ? 接下來(lái),我們首先分析AssetManager類(lèi)的構(gòu)造函數(shù)和成員函數(shù)addAssetPath的實(shí)現(xiàn),接著再分析Resources類(lèi)的構(gòu)造函數(shù)的實(shí)現(xiàn),以便可以了解用來(lái)訪問(wèn)應(yīng)用程序資源的AssetManager對(duì)象和Resources對(duì)象的創(chuàng)建以及初始化過(guò)程。

? ? ? ? Step 4. new?AssetManager

public final class AssetManager {......private static AssetManager sSystem = null;......public AssetManager() {synchronized (this) {......init();......ensureSystemAssets();}}private static void ensureSystemAssets() {synchronized (sSync) {if (sSystem == null) {AssetManager system = new AssetManager(true);system.makeStringBlocks(false);sSystem = system;}}}...... }? ? ? ? 這個(gè)函數(shù)定義在文件frameworks/base/core/java/android/content/res/AssetManager.java中。

? ? ? ??AssetManager類(lèi)的構(gòu)造函數(shù)是通過(guò)調(diào)用另外一個(gè)成員函數(shù)init來(lái)執(zhí)行初始化工作的。在初始化完成當(dāng)前正在創(chuàng)建的AssetManager對(duì)象之后,AssetManager類(lèi)的構(gòu)造函數(shù)還會(huì)調(diào)用另外一個(gè)成員函數(shù)ensureSystemAssets來(lái)檢查當(dāng)前進(jìn)程是否已經(jīng)創(chuàng)建了一個(gè)用來(lái)訪問(wèn)系統(tǒng)資源的AssetManager對(duì)象。

? ? ? ? 如果用來(lái)訪問(wèn)系統(tǒng)資源的AssetManager對(duì)象還沒(méi)有創(chuàng)建的話,那么AssetManager類(lèi)的成員函數(shù)ensureSystemAssets就會(huì)創(chuàng)建并且初始化它,并且將它保存在AssetManager類(lèi)的靜態(tài)成員變量sSystem中。注意,創(chuàng)建用來(lái)訪問(wèn)系統(tǒng)資源和應(yīng)用程序資源的AssetManager對(duì)象的過(guò)程是一樣的,區(qū)別只在于它們所要訪問(wèn)的Apk文件不一樣,因此,接下來(lái)我們就只分析用來(lái)訪問(wèn)應(yīng)用資源的AssetManager對(duì)象的創(chuàng)建過(guò)程以及初始化過(guò)程。

? ? ? ?Step 5.?AssetManager.init

public final class AssetManager {......private int mObject;......private native final void init();...... }

? ? ? ?這個(gè)函數(shù)定義在文件frameworks/base/core/java/android/content/res/AssetManager.java中。

? ? ? ?AssetManager類(lèi)的成員函數(shù)init是一個(gè)JNI函數(shù),它是由C++層的函數(shù)android_content_AssetManager_init來(lái)實(shí)現(xiàn)的:

static void android_content_AssetManager_init(JNIEnv* env, jobject clazz) {AssetManager* am = new AssetManager();.....am->addDefaultAssets();......env->SetIntField(clazz, gAssetManagerOffsets.mObject, (jint)am); }? ? ? ? ?這個(gè)函數(shù)定義在文件frameworks/base/core/jni/android_util_AssetManager.cpp中。

? ? ? ? ?函數(shù)android_content_AssetManager_init首先創(chuàng)建一個(gè)C++層的AssetManager對(duì)象,接著調(diào)用這個(gè)C++層的AssetManager對(duì)象的成員函數(shù)addDefaultAssets來(lái)添加默認(rèn)的資源路徑,最后將這個(gè)這個(gè)C++層的AssetManager對(duì)象的地址保存在參數(shù)clazz所描述的一個(gè)Java層的AssetManager對(duì)象的成員變量mObject中。

? ? ? ? Step 6.?AssetManager.addDefaultAssets

static const char* kSystemAssets = "framework/framework-res.apk"; ......bool AssetManager::addDefaultAssets() {const char* root = getenv("ANDROID_ROOT");......String8 path(root);path.appendPath(kSystemAssets);return addAssetPath(path, NULL); }? ? ? ?這個(gè)函數(shù)定義在文件frameworks/base/libs/utils/AssetManager.cpp中。

? ? ? ?AssetManager類(lèi)的成員函數(shù)addDefaultAssets首先通過(guò)環(huán)境變量ANDROID_ROOT來(lái)獲得Android的系統(tǒng)路徑,接著再將全局變量kSystemAssets所指向的字符串“framework/framework-res.apk”附加到這個(gè)系統(tǒng)路徑的后面去,這樣就可以得到系統(tǒng)資源文件framework-res.apk的絕對(duì)路徑了。一般來(lái)說(shuō),環(huán)境變量ANDROID_ROOT所設(shè)置的Android系統(tǒng)路徑就是“/system”,因此,最終得到的系統(tǒng)資源文件的絕對(duì)路徑就為“/system/framework/framework-res.apk”。

? ? ? ?得到了系統(tǒng)資源文件framework-res.apk的絕對(duì)路徑之后,就調(diào)用AssetManager類(lèi)的成員函數(shù)addAssetPath來(lái)將它添加到當(dāng)前正在初始化的AssetManager對(duì)象中去。

? ? ? ?Step 7.?AssetManager.addAssetPath

static const char* kAppZipName = NULL; //"classes.jar"; ......bool AssetManager::addAssetPath(const String8& path, void** cookie) {AutoMutex _l(mLock);asset_path ap;String8 realPath(path);if (kAppZipName) {realPath.appendPath(kAppZipName);}ap.type = ::getFileType(realPath.string());if (ap.type == kFileTypeRegular) {ap.path = realPath;} else {ap.path = path;ap.type = ::getFileType(path.string());if (ap.type != kFileTypeDirectory && ap.type != kFileTypeRegular) {......return false;}}// Skip if we have it already.for (size_t i=0; i<mAssetPaths.size(); i++) {if (mAssetPaths[i].path == ap.path) {if (cookie) {*cookie = (void*)(i+1);}return true;}}......mAssetPaths.add(ap);// new paths are always added at the endif (cookie) {*cookie = (void*)mAssetPaths.size();}// add overlay packages for /system/framework; apps are handled by the// (Java) package managerif (strncmp(path.string(), "/system/framework/", 18) == 0) {// When there is an environment variable for /vendor, this// should be changed to something similar to how ANDROID_ROOT// and ANDROID_DATA are used in this file.String8 overlayPath("/vendor/overlay/framework/");overlayPath.append(path.getPathLeaf());if (TEMP_FAILURE_RETRY(access(overlayPath.string(), R_OK)) == 0) {asset_path oap;oap.path = overlayPath;oap.type = ::getFileType(overlayPath.string());bool addOverlay = (oap.type == kFileTypeRegular); // only .apks supported as overlayif (addOverlay) {oap.idmap = idmapPathForPackagePath(overlayPath);if (isIdmapStaleLocked(ap.path, oap.path, oap.idmap)) {addOverlay = createIdmapFileLocked(ap.path, oap.path, oap.idmap);}}if (addOverlay) {mAssetPaths.add(oap);} ......}}return true; }? ? ? ? 這個(gè)函數(shù)定義在文件frameworks/base/libs/utils/AssetManager.cpp中。

? ? ? ? 如果全局變量kAppZipName的值不等于NULL的話,那么它的值一般就是被設(shè)置為“classes.jar”,這時(shí)候就表示應(yīng)用程序的資源文件是保存在參數(shù)path所描述的一個(gè)目錄下的一個(gè)classes.jar文件中。全局變量kAppZipName的值一般被設(shè)置為NULL,并且參數(shù)path指向的是一個(gè)Apk文件,因此,接下來(lái)我們只考慮應(yīng)用程序資源不是保存在一個(gè)classes.jar文件的情況。

? ? ? ??AssetManager類(lèi)的成員函數(shù)addAssetPath首先是要檢查參數(shù)path指向的是一個(gè)文件或者目錄,并且該文件或者目錄存在,否則的話,它就會(huì)直接返回一個(gè)false值,而不會(huì)再繼續(xù)往下處理了。

? ? ? ??AssetManager類(lèi)的成員函數(shù)addAssetPath接著再檢查在其成員變量mAssetPaths所描述的一個(gè)類(lèi)型為asset_path的Vector中是否已經(jīng)添加過(guò)參數(shù)path所描述的一個(gè)Apk文件路徑了。如果已經(jīng)添加過(guò)了,那么AssetManager類(lèi)的成員函數(shù)addAssetPath就不會(huì)再繼續(xù)往下處理了,而是將與參數(shù)path所描述的一個(gè)Apk文件路徑所對(duì)應(yīng)的一個(gè)Cookie返回給調(diào)用者,即保存在輸出參數(shù)cookie中,前提是參數(shù)cookie的值不等于NULL。一個(gè)Apk文件路徑所對(duì)應(yīng)的Cookie實(shí)際上只是一個(gè)整數(shù),這個(gè)整數(shù)表示該Apk文件路徑所對(duì)應(yīng)的一個(gè)asset_path對(duì)象在成員變量mAssetPaths所描述的一個(gè)Vector中的索引再加上1。

? ? ? ? 經(jīng)過(guò)上面的檢查之后,AssetManager類(lèi)的成員函數(shù)addAssetPath確保參數(shù)path所描述的一個(gè)Apk文件路徑之前沒(méi)有被添加過(guò),于是接下來(lái)就會(huì)將與該Apk文件路徑所對(duì)應(yīng)的一個(gè)asset_path對(duì)象保存在成員變量mAssetPaths所描述的一個(gè)Vector的最末尾位置上,并且將這時(shí)候得到的Vector的大小作為一個(gè)Cookie值保存在輸出參數(shù)cookie中返回給調(diào)用者。

? ? ? ??AssetManager類(lèi)的成員函數(shù)addAssetPath的最后一個(gè)工作是檢查剛剛添加的Apk文件路徑是否是保存在/system/framework/目錄下面的。如果是的話,那么就會(huì)在/vendor/overlay/framework/目錄下找到一個(gè)同名的Apk文件,并且也會(huì)將該Apk文件添加到成員變量mAssetPaths所描述的一個(gè)Vector中去。這是一種資源覆蓋機(jī)制,手機(jī)廠商可以利用它來(lái)自定義的系統(tǒng)資源,即用自定義的系統(tǒng)資源來(lái)覆蓋系統(tǒng)默認(rèn)的系統(tǒng)資源,以達(dá)到個(gè)性化系統(tǒng)界面的目的。

? ? ? ? 如果手機(jī)廠商要利用上述的資源覆蓋機(jī)制來(lái)自定義自己的系統(tǒng)資源,那么還需要提供一個(gè)idmap文件,用來(lái)說(shuō)明它在/vendor/overlay/framework/目錄提供的Apk文件要覆蓋系統(tǒng)的哪些默認(rèn)資源,使用資源ID來(lái)描述,因此,這個(gè)idmap文件實(shí)際上就是一個(gè)資源ID映射文件。這個(gè)idmap文件最終保存在/data/resource-cache/目錄下,并且按照一定的格式來(lái)命令,例如,假設(shè)手機(jī)廠商提供的覆蓋資源文件為/vendor/overlay/framework/framework-res.apk,那么對(duì)應(yīng)的idmap文件就會(huì)以名稱(chēng)為@vendor@overlay@framework@framework-res.apk@idmap的形式保存在目錄/data/resource-cache/下。

? ? ? ? 關(guān)于Android系統(tǒng)的資源覆蓋(Overlay)機(jī)制,可以參考frameworks/base/libs/utils目錄下的READ文件。

? ? ? ? 這一步執(zhí)行完成之后,回到前面的Step 3中,即ActivityThread類(lèi)的成員函數(shù)getTopLevelResources中,接下來(lái)它就會(huì)調(diào)用前面所創(chuàng)建的Java層的AssetManager對(duì)象的成員函數(shù)addAssetPath來(lái)添加指定的應(yīng)用程序資源文件路徑。

? ? ? ? Step 8.?AssetManager.addAssetPath

public final class AssetManager {....../*** Add an additional set of assets to the asset manager. This can be* either a directory or ZIP file. Not for use by applications. Returns* the cookie of the added asset, or 0 on failure.* {@hide}*/public native final int addAssetPath(String path);...... }

? ? ? ? 這個(gè)函數(shù)定義在文件frameworks/base/core/java/android/content/res/AssetManager.java中。

? ? ? ??AssetManager類(lèi)的成員函數(shù)addAssetPath是一個(gè)JNI方法,它是由C++層的函數(shù)android_content_AssetManager_addAssetPath來(lái)實(shí)現(xiàn)的,如下所示:

static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz,jstring path) {......AssetManager* am = assetManagerForJavaObject(env, clazz);......const char* path8 = env->GetStringUTFChars(path, NULL);void* cookie;bool res = am->addAssetPath(String8(path8), &cookie);env->ReleaseStringUTFChars(path, path8);return (res) ? (jint)cookie : 0; }? ? ? ? 這個(gè)函數(shù)定義在文件frameworks/base/core/jni/android_util_AssetManager.cpp中。

? ? ? ? 參數(shù)clazz指向的是Java層的一個(gè)AssetManager對(duì)象,函數(shù)android_content_AssetManager_addAssetPath首先調(diào)用另外一個(gè)函數(shù)assetManagerForJavaObject來(lái)將它的成員函數(shù)mObject轉(zhuǎn)換為一個(gè)C++層的AssetManager對(duì)象。有了這個(gè)C++層的AssetManager對(duì)象之后,就可以調(diào)用它的成員函數(shù)addAssetPath來(lái)將參數(shù)path所描述的一個(gè)Apk文件路徑添加到它里面去了,這個(gè)過(guò)程可以參考前面的Step 7。

? ? ? ? 這一步執(zhí)行完成之后,回到前面的Step 3中,即ActivityThread類(lèi)的成員函數(shù)getTopLevelResources中,接下來(lái)就會(huì)根據(jù)前面所創(chuàng)建的Java層的AssetManager對(duì)象來(lái)創(chuàng)建一個(gè)Resources對(duì)象。

? ? ? ? Step 9. new Resources

public class Resources {....../*package*/ final AssetManager mAssets;......public Resources(AssetManager assets, DisplayMetrics metrics,Configuration config, CompatibilityInfo compInfo) {mAssets = assets;......updateConfiguration(config, metrics);assets.ensureStringBlocks();}...... }? ? ? ? 這個(gè)函數(shù)定義在文件frameworks/base/core/java/android/content/res/Resources.java中。

? ? ? ??Resources類(lèi)的構(gòu)造函數(shù)首先將參數(shù)assets所指向的一個(gè)AssetManager對(duì)象保存在成員變量mAssets中,以便以后可以通過(guò)它來(lái)訪問(wèn)應(yīng)用程序的資源,接下來(lái)調(diào)用另外一個(gè)成員函數(shù)updateConfiguration來(lái)設(shè)置設(shè)備配置信息,最后調(diào)用參數(shù)assets所指向的一個(gè)AssetManager對(duì)象的成員函數(shù)ensureStringBlocks來(lái)創(chuàng)建字符串資源池。

? ? ? ? 接下來(lái),我們就首先分析Resources類(lèi)的成員函數(shù)updateConfiguration的實(shí)現(xiàn),接著再分析AssetManager類(lèi)的成員函數(shù)ensureStringBlocks的實(shí)現(xiàn)。

? ? ? ? Step 10.?Resources.updateConfiguration

public class Resources {......private final Configuration mConfiguration = new Configuration();......public void updateConfiguration(Configuration config,DisplayMetrics metrics) {synchronized (mTmpValue) {int configChanges = 0xfffffff;if (config != null) {configChanges = mConfiguration.updateFrom(config);}if (mConfiguration.locale == null) {mConfiguration.locale = Locale.getDefault();}if (metrics != null) {mMetrics.setTo(metrics);mMetrics.updateMetrics(mCompatibilityInfo,mConfiguration.orientation, mConfiguration.screenLayout);}mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;String locale = null;if (mConfiguration.locale != null) {locale = mConfiguration.locale.getLanguage();if (mConfiguration.locale.getCountry() != null) {locale += "-" + mConfiguration.locale.getCountry();}}int width, height;if (mMetrics.widthPixels >= mMetrics.heightPixels) {width = mMetrics.widthPixels;height = mMetrics.heightPixels;} else {//noinspection SuspiciousNameCombinationwidth = mMetrics.heightPixels;//noinspection SuspiciousNameCombinationheight = mMetrics.widthPixels;}int keyboardHidden = mConfiguration.keyboardHidden;if (keyboardHidden == Configuration.KEYBOARDHIDDEN_NO&& mConfiguration.hardKeyboardHidden== Configuration.HARDKEYBOARDHIDDEN_YES) {keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT;}mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,locale, mConfiguration.orientation,mConfiguration.touchscreen,(int)(mMetrics.density*160), mConfiguration.keyboard,keyboardHidden, mConfiguration.navigation, width, height,mConfiguration.screenLayout, mConfiguration.uiMode, sSdkVersion);......}...... }...... }? ? ? ? 這個(gè)函數(shù)定義在文件frameworks/base/core/java/android/content/res/Resources.java中。

? ? ? ??Resources類(lèi)的成員變量mConfiguration指向的是一個(gè)Configuration對(duì)象,用來(lái)描述設(shè)備當(dāng)前的配置信息,這些配置信息對(duì)應(yīng)的就是在前面Android資源管理框架(Asset Manager)簡(jiǎn)要介紹和學(xué)習(xí)計(jì)劃一文中提到18個(gè)資源維度。

? ? ? ??Resources類(lèi)的成員函數(shù)updateConfiguration首先是根據(jù)參數(shù)config和metrics來(lái)更新設(shè)備的當(dāng)前配置信息,例如,屏幕大小和密碼、國(guó)家地區(qū)和語(yǔ)言、鍵盤(pán)配置情況等等,接著再調(diào)用成員變量mAssets所指向的一個(gè)Java層的AssetManager對(duì)象的成員函數(shù)setConfiguration來(lái)將這些配置信息設(shè)置到與之關(guān)聯(lián)的C++層的AssetManager對(duì)象中去。

? ? ? ? 接下來(lái),我們就繼續(xù)分析AssetManager類(lèi)的成員函數(shù)setConfiguration的實(shí)現(xiàn),以便可以了解設(shè)備配置信息的設(shè)置過(guò)程。

? ? ? ? Step 11.?AssetManager.setConfiguration

public final class AssetManager {....../*** Change the configuation used when retrieving resources. Not for use by* applications.* {@hide}*/public native final void setConfiguration(int mcc, int mnc, String locale,int orientation, int touchscreen, int density, int keyboard,int keyboardHidden, int navigation, int screenWidth, int screenHeight,int screenLayout, int uiMode, int majorVersion);...... }? ? ? ? 這個(gè)函數(shù)定義在文件frameworks/base/core/java/android/content/res/AssetManager.java中。

? ? ? ??AssetManager類(lèi)的成員函數(shù)setConfiguration是一個(gè)JNI方法,它是由C++層的函數(shù)android_content_AssetManager_setConfiguration來(lái)實(shí)現(xiàn)的,如下所示:

static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject clazz,jint mcc, jint mnc,jstring locale, jint orientation,jint touchscreen, jint density,jint keyboard, jint keyboardHidden,jint navigation,jint screenWidth, jint screenHeight,jint screenLayout, jint uiMode,jint sdkVersion) {AssetManager* am = assetManagerForJavaObject(env, clazz);if (am == NULL) {return;}ResTable_config config;memset(&config, 0, sizeof(config));const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL;config.mcc = (uint16_t)mcc;config.mnc = (uint16_t)mnc;config.orientation = (uint8_t)orientation;config.touchscreen = (uint8_t)touchscreen;config.density = (uint16_t)density;config.keyboard = (uint8_t)keyboard;config.inputFlags = (uint8_t)keyboardHidden;config.navigation = (uint8_t)navigation;config.screenWidth = (uint16_t)screenWidth;config.screenHeight = (uint16_t)screenHeight;config.screenLayout = (uint8_t)screenLayout;config.uiMode = (uint8_t)uiMode;config.sdkVersion = (uint16_t)sdkVersion;config.minorVersion = 0;am->setConfiguration(config, locale8);if (locale != NULL) env->ReleaseStringUTFChars(locale, locale8); }? ? ? ? 這個(gè)函數(shù)定義在文件frameworks/base/core/jni/android_util_AssetManager.cpp中。

? ? ? ? 參數(shù)clazz指向的是一個(gè)Java層的AssetManager對(duì)象,函數(shù)android_content_AssetManager_setConfiguration首先調(diào)用另外一個(gè)函數(shù)assetManagerForJavaObject將它的成員變量mObject轉(zhuǎn)換為一個(gè)C++層的AssetManager對(duì)象。

? ? ? ? 函數(shù)android_content_AssetManager_setConfiguration接下來(lái)再根據(jù)其它參數(shù)來(lái)創(chuàng)建一個(gè)ResTable_config對(duì)象,這個(gè)ResTable_config對(duì)象就用來(lái)描述設(shè)備的當(dāng)前配置信息。

? ? ? ? 函數(shù)android_content_AssetManager_setConfiguration最后調(diào)用前面獲得C++層的AssetManager對(duì)象的成員函數(shù)setConfiguration來(lái)將前面創(chuàng)建的ResTable_config對(duì)象設(shè)置到它內(nèi)部去,以便C++層的AssetManager對(duì)象可以根據(jù)設(shè)備的當(dāng)前配置信息來(lái)找到最合適的資源。

? ? ? ? Step 12.?AssetManager.setConfiguration

void AssetManager::setConfiguration(const ResTable_config& config, const char* locale) {AutoMutex _l(mLock);*mConfig = config;if (locale) {setLocaleLocked(locale);} else if (config.language[0] != 0) {char spec[9];spec[0] = config.language[0];spec[1] = config.language[1];if (config.country[0] != 0) {spec[2] = '_';spec[3] = config.country[0];spec[4] = config.country[1];spec[5] = 0;} else {spec[3] = 0;}setLocaleLocked(spec);} else {updateResourceParamsLocked();} }? ? ? ? 這個(gè)函數(shù)定義在文件frameworks/base/libs/utils/AssetManager.cpp中。

? ? ? ? AssetManager類(lèi)的成員變量mConfig指向的是一個(gè)ResTable_config對(duì)象,用來(lái)描述設(shè)備的當(dāng)前配置信息,AssetManager類(lèi)的成員函數(shù)setConfiguration首先將參數(shù)config所描述的設(shè)備配置信息拷貝到它里面去。

? ? ? ? 如果參數(shù)local的值不等于NULL,那么它指向的字符串就是用來(lái)描述設(shè)備的國(guó)家、地區(qū)和語(yǔ)言信息的,這時(shí)候AssetManager類(lèi)的成員函數(shù)setConfiguration就會(huì)調(diào)用另外一個(gè)成員函數(shù)setLocalLocked來(lái)將它們?cè)O(shè)置到AssetManager類(lèi)的另外一個(gè)成員變量mLocale中去。

? ? ? ? 如果參數(shù)local的值等于NULL,并且參數(shù)config指向的一個(gè)ResTable_config對(duì)象包含了設(shè)備的國(guó)家、地區(qū)和語(yǔ)言信息,那么AssetManager類(lèi)的成員函數(shù)setConfiguration同樣會(huì)調(diào)用另外一個(gè)成員函數(shù)setLocalLocked來(lái)將它們?cè)O(shè)置到AssetManager類(lèi)的另外一個(gè)成員變量mLocale中去。

? ? ? ? 如果參數(shù)local的值等于NULL,并且參數(shù)config指向的一個(gè)ResTable_config對(duì)象沒(méi)有包含設(shè)備的國(guó)家、地區(qū)和語(yǔ)言信息,那么就說(shuō)明設(shè)備的國(guó)家、地區(qū)和語(yǔ)言等信息不需要更新,這時(shí)候AssetManager類(lèi)的成員函數(shù)setConfiguration就會(huì)直接調(diào)用另外一個(gè)成員函數(shù)updateResourceParamsLocked來(lái)更新資源表中的設(shè)備配置信息。

? ? ? ? 注意,AssetManager類(lèi)的成員函數(shù)setLocalLocked來(lái)更新了成員變量mLocale的內(nèi)容之后,同樣會(huì)調(diào)用另外一個(gè)成員函數(shù)updateResourceParamsLocked來(lái)更新資源表中的設(shè)備配置信息。

? ? ? ??AssetManager類(lèi)的成員函數(shù)updateResourceParamsLocked的實(shí)現(xiàn)如下所示:

void AssetManager::updateResourceParamsLocked() const {ResTable* res = mResources;if (!res) {return;}size_t llen = mLocale ? strlen(mLocale) : 0;mConfig->language[0] = 0;mConfig->language[1] = 0;mConfig->country[0] = 0;mConfig->country[1] = 0;if (llen >= 2) {mConfig->language[0] = mLocale[0];mConfig->language[1] = mLocale[1];}if (llen >= 5) {mConfig->country[0] = mLocale[3];mConfig->country[1] = mLocale[4];}mConfig->size = sizeof(*mConfig);res->setParameters(mConfig); }? ? ? ??這個(gè)函數(shù)定義在文件frameworks/base/libs/utils/AssetManager.cpp中。

? ? ? ??AssetManager類(lèi)的成員變量mResources指向的是一個(gè)ResTable對(duì)象,這個(gè)ResTable對(duì)象描述的就是一個(gè)資源索引表。Android應(yīng)用程序的資源索引表的格式以及生成過(guò)程可以參考前面Android應(yīng)用程序資源的編譯和打包過(guò)程分析一文。

? ? ? ??AssetManager類(lèi)的成員函數(shù)updateResourceParamsLocked首先是將成員變量mLocale所描述的國(guó)家、地區(qū)和語(yǔ)言信息更新到另外一個(gè)成員變量mConfig中去,接著再將成員變量mConfig所包含的設(shè)備配置信息設(shè)置到成員變量mResources所描述的一個(gè)資源索引表中去,這是通過(guò)調(diào)用成員變量mResources所指向的一個(gè)ResTable對(duì)象的成員函數(shù)setParameters來(lái)實(shí)現(xiàn)的。

? ? ? ? 這一步執(zhí)行完成之后,返回到前面的Step 9中,即Resources類(lèi)的構(gòu)造函數(shù),接下來(lái)它就會(huì)調(diào)用AssetManager類(lèi)的成員函數(shù)ensureStringBlocks來(lái)創(chuàng)建字符串資源池。

? ? ? ? Step 13.?AssetManager.ensureStringBlocks

public final class AssetManager {......private StringBlock mStringBlocks[] = null;....../*package*/ final void ensureStringBlocks() {if (mStringBlocks == null) {synchronized (this) {if (mStringBlocks == null) {makeStringBlocks(true);}}}}...... }? ? ? ? 這個(gè)函數(shù)定義在文件frameworks/base/core/java/android/content/res/AssetManager.java中。

? ? ? ??AssetManager類(lèi)的成員變量mStringBlocks指向的是一個(gè)StringBlock數(shù)組,其中,每一個(gè)StringBlock對(duì)象都是用來(lái)描述一個(gè)字符串資源池。從前面Android應(yīng)用程序資源的編譯和打包過(guò)程分析一文可以知道,每一個(gè)資源表都包含有一個(gè)資源項(xiàng)值字符串資源池,AssetManager類(lèi)的成員變量mStringBlocks就是用來(lái)保存所有的資源表中的資源項(xiàng)值字符串資源池的。

? ? ? ??AssetManager類(lèi)的成員函數(shù)ensureStringBlocks首先檢查成員變量mStringBlocks的值是否等于null。如果等于null的話,那么就說(shuō)明當(dāng)前應(yīng)用程序使用的資源表中的資源項(xiàng)值字符串資源池還沒(méi)有讀取出來(lái),這時(shí)候就會(huì)調(diào)用另外一個(gè)成員函數(shù)makeStringBlocks來(lái)進(jìn)行讀取。

? ? ? ?Step 14.?AssetManager.makeStringBlocks

public final class AssetManager {......private final void makeStringBlocks(boolean copyFromSystem) {final int sysNum = copyFromSystem ? sSystem.mStringBlocks.length : 0;final int num = getStringBlockCount();mStringBlocks = new StringBlock[num];......for (int i=0; i<num; i++) {if (i < sysNum) {mStringBlocks[i] = sSystem.mStringBlocks[i];} else {mStringBlocks[i] = new StringBlock(getNativeStringBlock(i), true);}}}......private native final int getStringBlockCount();private native final int getNativeStringBlock(int block);...... }

? ? ? ??這個(gè)函數(shù)定義在文件frameworks/base/core/java/android/content/res/AssetManager.java中。

? ? ? ? 參數(shù)copyFromSystem表示是否要將系統(tǒng)資源表里面的資源項(xiàng)值字符串資源池也一起拷貝到成員變量mStringBlokcs所描述的一個(gè)數(shù)組中去。如果它的值等于true的時(shí)候,那么AssetManager就會(huì)首先獲得makeStringBlocks首先獲得系統(tǒng)資源表的個(gè)數(shù)sysNum,接著再獲得總的資源表個(gè)數(shù)num,這是通過(guò)調(diào)用JNI方法getStringBlockCount來(lái)實(shí)現(xiàn)的。注意,總的資源表個(gè)數(shù)num是包含了系統(tǒng)資源表的個(gè)數(shù)sysNum的。

? ? ? ? 從前面的Step 4可以知道,用來(lái)訪問(wèn)系統(tǒng)資源包的AssetManager對(duì)象就保存在AssetManager類(lèi)的靜態(tài)成員變量sSystem中,并且這個(gè)AssetManager對(duì)象是最先被創(chuàng)建以及初始化的。也就是說(shuō),當(dāng)執(zhí)行到這一步的時(shí)候,所有系統(tǒng)資源表的資源項(xiàng)值字符串資源池已經(jīng)讀取出來(lái),它們就保存在AssetManager類(lèi)的靜態(tài)成員變量sSystem所描述的一個(gè)AssetManager對(duì)象的成員變量mStringBlocks中,因此,只將它們拷貝到當(dāng)前正在處理的AssetManager對(duì)象的成員變量mStringBlokcs的前sysNum個(gè)位置上去就可以了。

? ? ? ? 最后,AssetManager類(lèi)的成員函數(shù)makeStringBlocks就調(diào)用另外一個(gè)JNI方法getNativeStringBlock來(lái)讀取剩余的其它資源表的資源項(xiàng)值字符串資源池,并且分別將它們封裝在一個(gè)StringBlock對(duì)象保存在成員變量mStringBlokcs所描述的一個(gè)數(shù)組中。

? ? ? ??AssetManager類(lèi)的JNI方法getNativeStringBlock實(shí)際上就是將每一個(gè)資源包里面的resources.arsc文件的資源項(xiàng)值字符串資源池?cái)?shù)據(jù)塊讀取出來(lái),并且封裝在一個(gè)C++層的StringPool對(duì)象中,然后AssetManager類(lèi)的成員函數(shù)makeStringBlocks再將該StringPool對(duì)象封裝成一個(gè)Java層的StringBlock中。關(guān)于資源表中的資源項(xiàng)值字符串資源池的更多信息,可以參考前面Android應(yīng)用程序資源的編譯和打包過(guò)程分析一文。

? ? ? ? 至此,我們就分析完成Android應(yīng)用程序資源管理器的創(chuàng)建的初始化過(guò)程了,主要就是創(chuàng)建和初始化用來(lái)訪問(wèn)應(yīng)用程序資源的AssetManager對(duì)象和Resources對(duì)象,其中,初始化操作包括設(shè)置AssetManager對(duì)象的資源文件路徑以及設(shè)備配置信息等。有了這兩個(gè)初始化的AssetManager對(duì)象和Resources對(duì)象之后,在接下來(lái)的一篇文章中,我們就可以繼續(xù)分析應(yīng)用程序資源的查找過(guò)程了,敬請(qǐng)關(guān)注!

老羅的新浪微博:http://weibo.com/shengyangluo,歡迎關(guān)注!

轉(zhuǎn)載于:https://www.cnblogs.com/wuyida/archive/2013/04/22/6300513.html

總結(jié)

以上是生活随笔為你收集整理的Android应用程序资源管理器(Asset Manager)的创建过程分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。