涉及源碼(Android 4.4.2):
/frameworks/base/core/java/android/app/ResourcesManager.java
/frameworks/base/core/jni/android_util_AssetManager.cpp
/core/java/android/content/res/AssetManager.java
/frameworks/base/libs/androidfw/AssetManager.cpp
我們知道,如果我們希望使用或者得到某些資源粹排,我們通常的做法是使用Context的getXXX方法,例如:
public final Drawable getDrawable(int id) {
return getResources().getDrawable(id, getTheme());
}
可以看到它首先需要獲取到Resources資源對象款筑,從資源對象中得到想要的資源。當(dāng)我們使用Context的getResources方法來獲取Resources對象的時(shí)候腾么,最終調(diào)用的是ResourcesManager的getTopLevelResources方法來獲取到對應(yīng)的Resources對象奈梳。
ResourcesManager采用的是單例模式,這就保證了所有的Context調(diào)用的都是同一個(gè)ResourcesManager對象的getTopLevelResources方法解虱,所以不同Context的getResources方法獲取是同一套資源對象攘须。
/frameworks/base/core/java/android/app/ResourcesManager.java
// 單例模式獲取ResourcesManager對象
public static ResourcesManager getInstance() {
synchronized (ResourcesManager.class) {
if (sResourcesManager == null) {
sResourcesManager = new ResourcesManager();
}
return sResourcesManager;
}
}
下面來看看ResourcesManager的getTopLevelResources方法
/frameworks/base/core/java/android/app/ResourcesManager.java
// 資源對象存放在ArrayMap的集合中,并且對象使用的是弱引用
final ArrayMap<ResourcesKey, WeakReference<Resources> > mActiveResources
= new ArrayMap<ResourcesKey, WeakReference<Resources> >();
public Resources getTopLevelResources(String resDir, int displayId,
Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) {
final float scale = compatInfo.applicationScale;
// 1殴泰、通過傳入的參數(shù)確定資源對象的key值
ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale,
token);
Resources r;
synchronized (this) {
// 2阻课、通過key值,從mActiveResources Map集合中獲取對應(yīng)的資源對象
WeakReference<Resources> wr = mActiveResources.get(key);
r = wr != null ? wr.get() : null;
// 3艰匙、如果資源對象不為空限煞,直接將其返回,否則執(zhí)行步驟4
if (r != null && r.getAssets().isUpToDate()) {
return r;
}
}
// 4员凝、創(chuàng)建AssetManager對象署驻,并且將資源目錄(實(shí)際為apk文件路徑)加入資源路徑
AssetManager assets = new AssetManager();
if (assets.addAssetPath(resDir) == 0) {
return null;
}
DisplayMetrics dm = getDisplayMetricsLocked(displayId);
Configuration config;
boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
final boolean hasOverrideConfig = key.hasOverrideConfiguration();
if (!isDefaultDisplay || hasOverrideConfig) {
config = new Configuration(getConfiguration());
if (!isDefaultDisplay) {
applyNonDefaultDisplayMetricsToConfigurationLocked(dm, config);
}
if (hasOverrideConfig) {
config.updateFrom(key.mOverrideConfiguration);
}
} else {
config = getConfiguration();
}
// 5、創(chuàng)建資源對象
r = new Resources(assets, dm, config, compatInfo, token);
// 6健霹、如果mActiveResources Map集合沒有該資源對象旺上,則將其加入,并將資源對象返回
synchronized (this) {
WeakReference<Resources> wr = mActiveResources.get(key);
Resources existing = wr != null ? wr.get() : null;
if (existing != null && existing.getAssets().isUpToDate()) {
r.getAssets().close();
return existing;
}
mActiveResources.put(key, new WeakReference<Resources>(r));
return r;
}
}
從上面可以總結(jié)以下幾點(diǎn):
1糖埋、Resources對象使用了一個(gè)ArrayMap對象進(jìn)行緩存宣吱,因此表明其內(nèi)部可能包含多個(gè)Resources對象。
2瞳别、Resources對象中包含一個(gè)AssetManager對象征候,資源的添加工作是通過該對象的addAssetPath完成的。
下面就來看看具體的資源資源添加的過程
AssetManager的創(chuàng)建
/core/java/android/content/res/AssetManager.java
public AssetManager() {
synchronized (this) {
// 1祟敛、調(diào)用init初始化方法
init();
// 2疤坝、保證系統(tǒng)資源對象的存在
ensureSystemAssets();
}
}
1、調(diào)用init初始化方法
init方法是一個(gè)native方法馆铁,它最終調(diào)用的是android_util_AssetManager.cpp中的android_content_AssetManager_init方法跑揉,下面就進(jìn)入native層進(jìn)行操作了,前面的操作都是在java層處理的。
/frameworks/base/core/jni/android_util_AssetManager.cpp
static void android_content_AssetManager_init(JNIEnv* env, jobject clazz)
{
// 1历谍、創(chuàng)建一個(gè)AssetManager對象
AssetManager* am = new AssetManager();
// 2现拒、添加默認(rèn)的資源
am->addDefaultAssets();
// 3、將AssetManager對象(C++對象)的引用保存在Java層AssetManager對象的mObject中
env->SetIntField(clazz, gAssetManagerOffsets.mObject, (jint)am);
}
(1) 添加系統(tǒng)默認(rèn)資源操作
/frameworks/base/libs/androidfw/AssetManager.cpp
static const char* kSystemAssets = "framework/framework-res.apk";
bool AssetManager::addDefaultAssets()
{
// 1望侈、得到系統(tǒng)目錄/system/
const char* root = getenv("ANDROID_ROOT");
String8 path(root);
// 2具练、得到系統(tǒng)資源的完整路徑/system/framework/framework-res.apk
path.appendPath(kSystemAssets);
// 3、將系統(tǒng)資源添加到資源路徑
return addAssetPath(path, NULL);
}
從上面可以看到甜无,默認(rèn)會(huì)將系統(tǒng)資源添加到資源路徑,這也是我們應(yīng)用可以訪問到系統(tǒng)資源的原因哥遮。
(2)添加AssetManager對象(C++對象)引用到Java層AssetManager對象的mObject上的操作
其實(shí)就是要弄清楚gAssetManagerOffsets.mObject的什么東西岂丘?
下面來看看android_util_AssetManager.cpp中register_android_content_AssetManager方法。
/frameworks/base/core/jni/android_util_AssetManager.cpp
int register_android_content_AssetManager(JNIEnv* env)
{
// 1眠饮、獲取到j(luò)ava層的AssetManager類
jclass assetManager = env->FindClass("android/content/res/AssetManager");
// 2奥帘、獲取java層的AssetManager類的mObject字段,并將其保存在gAssetManagerOffsets.mObject中
gAssetManagerOffsets.mObject
= env->GetFieldID(assetManager, "mObject", "I");
return AndroidRuntime::registerNativeMethods(env,
"android/content/res/AssetManager", gAssetManagerMethods, NELEM(gAssetManagerMethods));
}
從上面就可以知道仪召,gAssetManagerOffsets.mObject對應(yīng)就是java層的AssetManager類的mObject字段寨蹋。
2、保證系統(tǒng)資源對象的存在
/core/java/android/content/res/AssetManager.java
private static void ensureSystemAssets() {
synchronized (sSync) {
if (sSystem == null) {
AssetManager system = new AssetManager(true);
system.makeStringBlocks(false);
sSystem = system;
}
}
}
sSystem是一個(gè)靜態(tài)的AssetManager對象扔茅,在Zygote啟動(dòng)時(shí)已經(jīng)賦值了已旧,主要就是初次啟動(dòng)的時(shí)候會(huì)執(zhí)行,供系統(tǒng)使用召娜,上面創(chuàng)建AssetManager對象运褪,創(chuàng)建過程跟前面相同。
上面類之間的關(guān)系如下圖所示:
資源路徑的添加
從上圖可以看到玖瘸,在java層的AssetManager類中秸讹,addAssetPath方法會(huì)調(diào)用addAssetPathNative方法,addAssetPathNative方法是一個(gè)native方法雅倒,它對應(yīng)的就是android_util_AssetManager.cpp中的android_content_AssetManager_addAssetPath方法璃诀。
/frameworks/base/core/jni/android_util_AssetManager.cpp
static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz,
jstring path)
{
ScopedUtfChars path8(env, path);
if (path8.c_str() == NULL) {
return 0;
}
// 1、得到C++對象AssetManager對象
AssetManager* am = assetManagerForJavaObject(env, clazz);
if (am == NULL) {
return 0;
}
void* cookie;
// 2蔑匣、執(zhí)行addAssetPath方法
bool res = am->addAssetPath(String8(path8.c_str()), &cookie);
return (res) ? (jint)cookie : 0;
}
assetManagerForJavaObject方法就是拿到前面存放在java層的AssetManager類的mObject字段的值劣欢,它就是一個(gè)AssetManager的C++對象引用
AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject obj)
{
AssetManager* am = (AssetManager*)env->GetIntField(obj, gAssetManagerOffsets.mObject);
if (am != NULL) {
return am;
}
jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!");
return NULL;
}
/frameworks/base/libs/androidfw/AssetManager.cpp
// 資源路徑存放在一個(gè)Vector集合中
Vector<asset_path> mAssetPaths;
// 存放資源路徑的結(jié)構(gòu)體
struct asset_path
{
String8 path; // 路徑名
FileType type; // 文件類型
String8 idmap;
};
bool AssetManager::addAssetPath(const String8& path, void** cookie)
{
AutoMutex _l(mLock);
asset_path ap;
// 1、構(gòu)造一個(gè)asset_path結(jié)構(gòu)體裁良,其實(shí)就是確定路徑名和文件類型氧秘,并保存在結(jié)構(gòu)體中
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) {
ALOGW("Asset path %s is neither a directory nor file (type=%d).",
path.string(), (int)ap.type);
return false;
}
}
// 2、如果該路徑已經(jīng)添加到集合中趴久,則直接設(shè)置cookie為(索引+1)并返回丸相,否則進(jìn)入步驟3
for (size_t i=0; i<mAssetPaths.size(); i++) {
if (mAssetPaths[i].path == ap.path) {
if (cookie) {
*cookie = (void*)(i+1);
}
return true;
}
}
// 3、將路徑結(jié)構(gòu)體添加到集合中
mAssetPaths.add(ap);
// 4彼棍、將該路徑的集合size(索引+1)作為cookie的值
if (cookie) {
*cookie = (void*)mAssetPaths.size();
}
// 5灭忠、資源替換的過程膳算,這個(gè)暫不關(guān)注
// (Java) package manager
if (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 overlay
if (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);
} else {
ALOGW("failed to add overlay package %s\n", overlayPath.string());
}
}
}
return true;
}
如下圖所示: