每個apk有一個Resources
-
getTopLevelResources
synchronized (this) { // Resources is app scale dependent. if (DEBUG) Slog.w(TAG, "getTopLevelResources: " + resDir + " / " + scale); WeakReference<Resources> wr = mActiveResources.get(key); r = wr != null ? wr.get() : null; //if (r != null) Log.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate()); if (r != null && r.getAssets().isUpToDate()) { if (DEBUG) Slog.w(TAG, "Returning cached resources " + r + " " + resDir + ": appScale=" + r.getCompatibilityInfo().applicationScale + " key=" + key + " overrideConfig=" + overrideConfiguration); return r; }
- mActiveResources. ActivityThread類的成員變量mActiveResources指向的是一個HashMap。這個HashMap用來維護在當前應用程序進程中加載的每一個Apk文件及其對應的Resources對象的對應關系.
- 給定一個Apk文件路徑,ActivityThread類的成員函數getTopLevelResources可以在成員變量mActiveResources中檢查是否存在一個對應的Resources對象。如果不存在,那么就會新建一個,并且保存在ActivityThread類的成員變量mActiveResources中。
- 參數resDir即為要獲取其對應的Resources對象的Apk文件路徑
-
創建AssetManager對象
public AssetManager() { synchronized (this) { if (DEBUG_REFS) { mNumRefs = 0; incRefsLocked(this.hashCode()); } init(false); if (localLOGV) Log.v(TAG, "New asset manager: " + this); ensureSystemAssets(); } }
先調用了init(false)方法。而init通過jni調用到了底層。
AssetManager的文件定義在
android-6.0.0_r1\frameworks\base\core\java\android\content\res\AssetManager.java
中。而c++層的函數定義在frameworks/base/core/jni/android_util_AssetManager.cpp
中static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem) { if (isSystem) { verifySystemIdmaps(); } AssetManager* am = new AssetManager(); if (am == NULL) { jniThrowException(env, "java/lang/OutOfMemoryError", ""); return; } am->addDefaultAssets(); ALOGV("Created AssetManager %p for Java object %p\n", am, clazz); env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast<jlong>(am)); }
-
這里先創建了一個AssetManager對象。AssetManager.cpp定義在
android-6.0.0_r1\frameworks\base\libs\androidfw\AssetManager.cpp
中。-
然后進入到addDefaultAssets()方法。該方法添加默認的資源路徑
bool AssetManager::addDefaultAssets() { const char* root = getenv("ANDROID_ROOT"); LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_ROOT not set"); String8 path(root); path.appendPath(kSystemAssets); return addAssetPath(path, NULL); }
- s首先通過環境變量ANDROID_ROOT來獲得Android的系統路徑
- 接著再將全局變量kSystemAssets所指向的字符串“framework/framework-res.apk”附加到這個系統路徑的后面去
- addAssetPath來將它添加到當前正在初始化的AssetManager對象中去。
-
然后執行
env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast<jlong>(am));
將c++層生成的AssetManager設置到java層的mObject中// For communication with native code. private long mObject;
-
到這里AssetManager創建完畢。然后設置相關的路徑
AssetManager assets = new AssetManager(); // resDir can be null if the 'android' package is creating a new Resources object. // This is fine, since each AssetManager automatically loads the 'android' package // already. if (resDir != null) { if (assets.addAssetPath(resDir) == 0) { return null; } } if (splitResDirs != null) { for (String splitResDir : splitResDirs) { if (assets.addAssetPath(splitResDir) == 0) { return null; } } } if (overlayDirs != null) { for (String idmapPath : overlayDirs) { assets.addOverlayPath(idmapPath); } } if (libDirs != null) { for (String libDir : libDirs) { if (libDir.endsWith(".apk")) { // Avoid opening files we know do not have resources, // like code-only .jar files. if (assets.addAssetPath(libDir) == 0) { Log.w(TAG, "Asset path '" + libDir + "' does not exist or contains no resources."); } } } }
接著就創建Resource對象
r = new Resources(assets, dm, config, compatInfo);
這里看到AssetManager保存到了Resources對象中。接著進入到Resources的構造方法中
public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config, CompatibilityInfo compatInfo) { mAssets = assets; mMetrics.setToDefaults(); if (compatInfo != null) { mCompatibilityInfo = compatInfo; } updateConfiguration(config, metrics); assets.ensureStringBlocks(); }
最后進入到
updateConfiguration(Configuration config, DisplayMetrics metrics, CompatibilityInfo compat)
mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc, locale, mConfiguration.orientation, mConfiguration.touchscreen, mConfiguration.densityDpi, mConfiguration.keyboard, keyboardHidden, mConfiguration.navigation, width, height, mConfiguration.smallestScreenWidthDp, mConfiguration.screenWidthDp, mConfiguration.screenHeightDp, mConfiguration.screenLayout, mConfiguration.uiMode, Build.VERSION.RESOURCES_SDK_INT);
mConfiguration指向的是一個Configuration對象,用來描述設備當前的配置信息
Resources類的成員函數updateConfiguration首先是根據參數config和metrics來更新設備的當前配置信息,例如,屏幕大小和密碼、國家地區和語言、鍵盤配置情況等等,接著再調用成員變量mAssets所指向的一個Java層的AssetManager對象的成員函數setConfiguration來將這些配置信息設置到與之關聯的C++層的AssetManager對象中去。
-
進入到c++層中的定義
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 smallestScreenWidthDp, jint screenWidthDp, jint screenHeightDp, 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; // Constants duplicated from Java class android.content.res.Configuration. static const jint kScreenLayoutRoundMask = 0x300; static const jint kScreenLayoutRoundShift = 8; 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.smallestScreenWidthDp = (uint16_t)smallestScreenWidthDp; config.screenWidthDp = (uint16_t)screenWidthDp; config.screenHeightDp = (uint16_t)screenHeightDp; config.screenLayout = (uint8_t)screenLayout; config.uiMode = (uint8_t)uiMode; config.sdkVersion = (uint16_t)sdkVersion; config.minorVersion = 0; // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer // in C++. We must extract the round qualifier out of the Java screenLayout and put it // into screenLayout2. config.screenLayout2 = (uint8_t)((screenLayout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); am->setConfiguration(config, locale8); if (locale != NULL) env->ReleaseStringUTFChars(locale, locale8); }
將ResTable_config設置到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[RESTABLE_MAX_LOCALE_LEN]; config.getBcp47Locale(spec); setLocaleLocked(spec); } else { updateResourceParamsLocked(); } }
AssetManager類的成員變量mConfig指向的是一個ResTable_config對象,用來描述設備的當前配置信息,AssetManager類的成員函數setConfiguration首先將參數config所描述的設備配置信息拷貝到它里面去。
local的值不等于NULL,那么它指向的字符串就是用來描述設備的國家、地區和語言信息的,這時候AssetManager類的成員函數setConfiguration就會調用另外一個成員函數setLocalLocked來將它們設置到AssetManager類的另外一個成員變量mLocale中去。
local的值等于NULL,并且參數config指向的一個ResTable_config對象包含了設備的國家、地區和語言信息,那么AssetManager類的成員函數setConfiguration同樣會調用另外一個成員函數setLocalLocked來將它們設置到AssetManager類的另外一個成員變量mLocale中去。
如果參數local的值等于NULL,并且參數config指向的一個ResTable_config對象沒有包含設備的國家、地區和語言信息,那么就說明設備的國家、地區和語言等信息不需要更新,這時候AssetManager類的成員函數setConfiguration就會直接調用另外一個成員函數updateResourceParamsLocked來更新資源表中的設備配置信息。
注意,AssetManager類的成員函數setLocalLocked來更新了成員變量mLocale的內容之后,同樣會調用另外一個成員函數updateResourceParamsLocked來更新資源表中的設備配置信息。
-
updateResourceParamsLocked()方法
void AssetManager::updateResourceParamsLocked() const { ResTable* res = mResources; if (!res) { return; } if (mLocale) { mConfig->setBcp47Locale(mLocale); } else { mConfig->clearLocale(); } res->setParameters(mConfig); }
- AssetManager類的成員變量mResources指向的是一個ResTable對象,這個ResTable對象描述的就是一個資源索引表
- AssetManager類的成員函數updateResourceParamsLocked首先是將成員變量mLocale所描述的國家、地區和語言信息更新到另外一個成員變量mConfig中去,接著再將成員變量mConfig所包含的設備配置信息設置到成員變量mResources所描述的一個資源索引表中去,這是通過調用成員變量mResources所指向的一個ResTable對象的成員函數setParameters來實現的。
updateConfiguration執行完后就調用到
assets.ensureStringBlocks()
/*package*/ final void ensureStringBlocks() { if (mStringBlocks == null) { synchronized (this) { if (mStringBlocks == null) { makeStringBlocks(sSystem.mStringBlocks); } } } }
mStringBlocks指向的是一個StringBlock數組,其中,每一個StringBlock對象都是用來描述一個字符串資源池。每一個資源表都包含有一個資源項值字符串資源池,AssetManager類的成員變量mStringBlocks就是用來保存所有的資源表中的資源項值字符串資源池的.
-
ensureStringBlocks首先檢查成員變量mStringBlocks的值是否等于null。如果等于null的話,那么就說明當前應用程序使用的資源表中的資源項值字符串資源池還沒有讀取出來,這時候就會調用另外一個成員函數makeStringBlocks來進行讀取。
/*package*/ final void makeStringBlocks(StringBlock[] seed) { final int seedNum = (seed != null) ? seed.length : 0; final int num = getStringBlockCount(); mStringBlocks = new StringBlock[num]; if (localLOGV) Log.v(TAG, "Making string blocks for " + this + ": " + num); for (int i=0; i<num; i++) { if (i < seedNum) { mStringBlocks[i] = seed[i]; } else { mStringBlocks[i] = new StringBlock(getNativeStringBlock(i), true); } } }
-
getStringBlockCount
獲取字符串資源池的數量。這里又通過jni進行了調用static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz) { AssetManager* am = assetManagerForJavaObject(env, clazz); if (am == NULL) { return 0; } return am->getResources().getTableCount(); }
const ResTable& AssetManager::getResources(bool required) const { const ResTable* rt = getResTable(required); return *rt; }
通過循環先將系統的資源添加到數組中,然后再講剩余的資源池添加到數組中。
-
到這里整個Resources和AssetManager的創建已經完成。
?
-