前面我写过一篇名为 Android资源管理器过程分析 & 插件化实现Hook 资源管理器的实现,在片文章中主要讲解了一些基础知识,其中包括:资源的分类、资源的打包过程、自定义资源的ID范围及使用场景、插件化框架Android-Framework-Plugin是如何实现Hook AssetsManager的等,主要讲解的是使用过程;而我们今天这一篇是对资源初始化过程的源码分析.
知识铺垫
Activity在进程加载过程中都会创建一个对应的ContextImpl,并通过调用ContextImpl对象的成员函数init()来初始化Activity组件运行上下文环境的工作,其中就包括创建Resources类和AssetsManager类用来访问对程序资源。
详细步骤
1.调用performLaunchActivity()创建Activity
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
...
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
...
Context appContext = createBaseContextForActivity(r, activity, r.displayId);
...
}
- 在performLaunchActivity中首先通过反射创建Activity对象
- 调用LoadedApk对象的makeApplication(),获取Activity所属应用的Application对象
- 为Activity创建Base Context也就是ContextImpl对象,AssetManager对象就是在这里创建的。
2.分析createBaseContextForActivity()的实现
private Context createBaseContextForActivity(ActivityClientRecord r,
final Activity activity, int activityDisplayId) {
...
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, displayId, r.overrideConfig/* { Multi-Window */, r.token/* Multi-Window } */);
appContext.setOuterContext(activity);
...
}
3.接下来分析ResourcesManager的getTopLevelResources()
private final ArrayMap<ResourcesKey, WeakReference<Resources> > mActiveResources =
new ArrayMap<>();
...
/**
* Creates the top level Resources for applications with the given compatibility info.
*
* @param resDir the resource directory.
* @param splitResDirs split resource directories.
* @param overlayDirs the resource overlay directories.
* @param libDirs the shared library resource dirs this app references.
* @param displayId display Id.
* @param overrideConfiguration override configurations.
* @param compatInfo the compatibility info. Must not be null.
*/
Resources getTopLevelResources(String resDir, String[] splitResDirs,
String[] overlayDirs, String[] libDirs, int displayId,
Configuration overrideConfiguration, CompatibilityInfo compatInfo) {
...
ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfigCopy, scale);
Resources r;
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;
}
}
...
AssetManager assets = new AssetManager();
if (resDir != null) {
if (assets.addAssetPath(resDir) == 0) {
return null;
}
}
...
r = new Resources(assets, dm, config, compatInfo);
...
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<>(r));
return r;
}
}
4.重点分析AssetManager以及Resources的构造函数
-
4.1 AssetManager的构造函数
public AssetManager() {
synchronized (this) {
...
init(false);
ensureSystemAssets();
}
}
4.1.1 调用native方法init()来创建并初始化Native层的AssetManager对象。
static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem) { ... AssetManager* am = new AssetManager(); ... am->addDefaultAssets(); env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast<jlong>(am)); }
4.1.1.1 在android_content_AssetManager_init()中首先创建Native层AssetManager对象
4.1.1.2 调用AssetManager对象的addDefaultAssets添加系统资源
bool AssetManager::addDefaultAssets() { const char* root = getenv("ANDROID_ROOT"); String8 path(root); path.appendPath(kSystemAssets); return addAssetPath(path, NULL); } bool AssetManager::addAssetPath(const String8& path, int32_t* cookie) { ... asset_path ap; ... for (size_t i=0; i<mAssetPaths.size(); i++) { if (mAssetPaths[i].path == ap.path) { if (cookie) { *cookie = static_cast<int32_t>(i+1); } return true; } } ... mAssetPaths.add(ap); // new paths are always added at the end if (cookie) { *cookie = static_cast<int32_t>(mAssetPaths.size()); } ... if (mResources != NULL) { appendPathToResTable(ap); } return true; }
- 在addDefaultAssets()中首先创建系统资源路径,一般ANDROID_ROOT环境变量为"/system"
- kSystemAssets为"framework/framework-res.apk",所以系统资源路径为"/system/framework/framework-res.apk"。然后调用addAssetPath()。
- 在addAssetPath()中,首先检查mAssetPaths中是否已经包含了当前资源路径对应的asset_path对象,如果已经存在,返回asset_path在
- mAssetPaths中的索引值+1,所以*cookie的值从1开始。
- 否则,将asset_path添加到mAssetPaths中,同时给*cookie赋值。
- 如果资源表不为NULL,将asset_path添加到资源表。
4.1.1.3 最后将Native层的AssetManager对象地址保存在相应的Java对象的mObject中
4.1.2调用ensureSystemAssets()来确保系统资源管理对象已经创建并初始化。
4.2 Resources的构造函数
public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config,
CompatibilityInfo compatInfo) {
mAssets = assets;
...
updateConfiguration(config, metrics);
assets.ensureStringBlocks();
}
- 在Reosurces的构造函数中,首先将之前创建的AssetManager对象保存到mAssets中。
- 调用updateConfiguration()更新设备配置信息,如设备屏幕信息、国家地区网络信息以及键盘配置信息等,最终会将这些信息
- 保存到Native层的AssetManager对象中去。
- 调用ensureStringBlocks将系统资源表以及应用资源表中的字符串资源池地址保存到AssetManager的mStringBlocks中。
网友评论