美文网首页
从ActivityThread 分析Resources和Asse

从ActivityThread 分析Resources和Asse

作者: AntCoding | 来源:发表于2019-03-10 15:03 被阅读0次

    前面我写过一篇名为 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中。

    This ALL! Thanks EveryBody!

    相关文章

      网友评论

          本文标题:从ActivityThread 分析Resources和Asse

          本文链接:https://www.haomeiwen.com/subject/gcbupqtx.html