资源的插件化原理

继Activity的插件化juejin.im/post/5c67da…之后, 现在我们来看看资源的插件化方法,这里以VirtualApk的实现方案为例进行讲解,资源的插件化方案分为两种:一种是合并资源方案,将插件的所有资源添加到宿主的Resources中,这种插件方案可以访问宿主的资源。另一种是构建插件资源方案,每个每个插件都构造出独立的Resources,这种方案不可以访问宿主资源。具体的代码逻辑在LoadedPlugin中,如下所示:

    protected Resources createResources(Context context, String packageName, File apk) throws Exception {
        if (Constants.COMBINE_RESOURCES) {
            return ResourcesManager.createResources(context, packageName, apk);
        } else {
            Resources hostResources = context.getResources();
            AssetManager assetManager = createAssetManager(context, apk);
            return new Resources(assetManager, hostResources.getDisplayMetrics(), hostResources.getConfiguration());
        }
    },
复制代码

createResources方法用于创建Resources,如是是资源合并方案,会调用ResourcesManager的createResources方法,其内部会得到包含宿主资源的AssetManager,再通过反射调用AsserManager的addAssetPath来添加插件资源,返回新的Resources,最后采用Hook的方式用新的Resources替换就的Resources。

public static synchronized Resources createResources(Context hostContext, String packageName, File apk) throws Exception {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return createResourcesForN(hostContext, packageName, apk);
        }
        
        Resources resources = ResourcesManager.createResourcesSimple(hostContext, apk.getAbsolutePath());
        ResourcesManager.hookResources(hostContext, resources);
        return resources;
    }
复制代码

创建新的Resources的代码如下:

    private static Resources createResourcesSimple(Context hostContext, String apk) throws Exception {
        Resources hostResources = hostContext.getResources();
        Resources newResources = null;
        AssetManager assetManager;
        Reflector reflector = Reflector.on(AssetManager.class).method("addAssetPath", String.class);
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            assetManager = AssetManager.class.newInstance();
            reflector.bind(assetManager);
            final int cookie1 = reflector.call(hostContext.getApplicationInfo().sourceDir);;
            if (cookie1 == 0) {
                throw new RuntimeException("createResources failed, can't addAssetPath for " + hostContext.getApplicationInfo().sourceDir);
            }
        } else {
            assetManager = hostResources.getAssets();
            reflector.bind(assetManager);
        }
        final int cookie2 = reflector.call(apk);
        if (cookie2 == 0) {
            throw new RuntimeException("createResources failed, can't addAssetPath for " + apk);
        }
        List<LoadedPlugin> pluginList = PluginManager.getInstance(hostContext).getAllLoadedPlugins();
        for (LoadedPlugin plugin : pluginList) {
            final int cookie3 = reflector.call(plugin.getLocation());
            if (cookie3 == 0) {
                throw new RuntimeException("createResources failed, can't addAssetPath for " + plugin.getLocation());
            }
        }
        if (isMiUi(hostResources)) {
            newResources = MiUiResourcesCompat.createResources(hostResources, assetManager);
        } else if (isVivo(hostResources)) {
            newResources = VivoResourcesCompat.createResources(hostContext, hostResources, assetManager);
        } else if (isNubia(hostResources)) {
            newResources = NubiaResourcesCompat.createResources(hostResources, assetManager);
        } else if (isNotRawResources(hostResources)) {
            newResources = AdaptationResourcesCompat.createResources(hostResources, assetManager);
        } else {
            // is raw android resources
            newResources = new Resources(assetManager, hostResources.getDisplayMetrics(), hostResources.getConfiguration());
        }
        // lastly, sync all LoadedPlugin to newResources
        for (LoadedPlugin plugin : pluginList) {
            plugin.updateResources(newResources);
        }
        
        return newResources;
    }
复制代码

执行addAssetPath方法之后,插件中的资源就合并到宿主资源中去了,形成新的Resources,之后采用hook技术用新的Resources去代替旧的资源。

 public static void hookResources(Context base, Resources resources) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return;
        }
        try {
            Reflector reflector = Reflector.with(base);
            reflector.field("mResources").set(resources);
            Object loadedApk = reflector.field("mPackageInfo").get();
            Reflector.with(loadedApk).field("mResources").set(resources);

            Object activityThread = ActivityThread.currentActivityThread();
            Object resManager;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                resManager = android.app.ResourcesManager.getInstance();
            } else {
                resManager = Reflector.with(activityThread).field("mResourcesManager").get();
            }
            Map<Object, WeakReference<Resources>> map = Reflector.with(resManager).field("mActiveResources").get();
            Object key = map.keySet().iterator().next();
            map.put(key, new WeakReference<>(resources));
        } catch (Exception e) {
            Log.w(TAG, e);
        }
    }
复制代码

接下来继续讲解第二种方式,如果采用构建插件资源方案,会先创建AssetManager,再创建Resources并将AssetManager作为参数传进去。createAssetManager方法如下:

 protected AssetManager createAssetManager(Context context, File apk) throws Exception {
        AssetManager am = AssetManager.class.newInstance();
        Reflector.with(am).method("addAssetPath", String.class).call(apk.getAbsolutePath());
        return am;
    }
复制代码

首先动态创建AssetManager,再反射调用AssetManager的addAssetPath方法来加载插件,这个AssetManager只包含插件资源,所以,该方法新创建的Resources是插件的资源。 这就是资源的加载过程,当然还需要开绿系统版本和手机型号的差异性,读者可以自行阅读VituralApk的源码。github.com/didi/Virtua…

转载于:https://juejin.im/post/5c9f1d8ae51d452bb91cf0e7

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值