前言

近期工作中遇到两个问题。

  • 换运用皮肤
  • 加载插件apk中的view

Android 换肤技能一文中现已详细说明了怎么进行运用换肤。而加载插件apk中的view,运用前文说到的换肤技能,竟然无法实现!仔细重新研究Android apk动态加载机制,有了新的发现,并且还能够提高插件加载功率

布局加载

Android 换肤技能文中说到的加载插件图片办法,无法加载插件的布局文件。怎样尝试都是失败。布局文件是资源中最复杂的,需求解析xml中的其它元素,虽然布局文件的id能够获取,但xml中其它元素的id或许其它关联性的东西依然无法获取,这应该就是加载插件布局文件失败的原因。

宿主运用无法直播加载插件中的xml布局文件,换一个思路,插件将xml解析成view,将view传递给宿主运用运用。

插件apk需求运用插件context才能正确加载view,宿主怎么生成插件context呢?

  public abstract Context createPackageContext(String packageName,
        @CreatePackageOptions int flags)
  context.createPackageContext(packageName, Context.CONTEXT_IGNORE_SECURITY | 
        Context.CONTEXT_INCLUDE_CODE);

运用上述办法可正确创立插件context。

除此之外还有一种办法(本人没有验证过),activity的工作主要是由ContextImpl来完结的, 它在activity中是一个叫做mBase的成员变量。注意到Context中有如下两个笼统办法,看起来是和资源有关的,实际上context就是经过它们来获取资源的,这两个笼统办法的真实实现在ContextImpl中。也即是说,只要咱们自己实现这两个办法,就能够处理资源问题了。咱们在代码中能够创立activity承继类,重写对应办法即可。详细可参考 下文

/** Return an AssetManager instance for your application's package. */
public abstract AssetManager getAssets();
/** Return a Resources instance for your application's package. */
public abstract Resources getResources();

动态加载方法

目前动态加载方法均是运用DexClassLoader方法获取对应的class实例,再运用反射调用对应接口,代码如下:

  DexClassLoader loader = new DexClassLoader(mPluginDir, getActivity().getApplicationInfo().dataDir, null, getClass().getClassLoader());
  String dex = getActivity().getDir("dex", 0).getAbsolutePath();
  String data = getActivity().getApplicationInfo().dataDir;
  Class<?> clazz = loader.loadClass("com.okunu.demoplugin.TailImpl");
  Constructor<?> constructor = clazz.getConstructor(new Class[] {});

这种方法存在一个问题,较为耗时,如果宿主管理着许多插件,这种加载方法就有问题,运用下面这种方法可加快插件的加载。

public void getTail2(Context pluginContext){
    try {
        Class clazz = pluginContext.getClassLoader().loadClass("com.okunu.demoplugin.TailImpl");
        Constructor<?> localConstructor = clazz.getConstructor(new Class[] {});
        Object obj = localConstructor.newInstance(new Object[] {});
        mTail = new IPluginProxy(clazz, obj);
    } catch (Exception e) {
        Log.i("okunu", "ee", e);
        e.printStackTrace();
    }
}

注意,一定要运用插件的context为参数,它和插件的其它类运用同一个classloader,既然能获取插件classloader,则能够获取插件中的其它类。如果不运用插件context为参数,则上述办法一定会报错。

总结

针对插件资源加载,其实分为两种方式。

  • 宿主直接运用插件资源,比方运用插件图片、字符串等
  • 宿主间接运用插件资源,比方在宿主中启动插件activity或许显现插件的view

第1种方式,能够在宿主运用中结构AssetManager,添加插件的资源途径。

第2种方式,宿主创立插件context并传递给插件,插件运用自己的context则可自在调用自己的资源了,怎么创立插件context前文胪陈了两种办法。

注意一点,宿主中肯定无法直接调用插件的R文件的。

动态加载apk,也有两种方法。

  • 运用DexClassLoader加载插件途径,获取插件的classLoader。
  • 运用现已创立好的插件context,获取插件的classLoader,效果和第1种相同,但速度要更快

动态加载apk机制还有许多东西能够深入研究,比方说插件activity的生命周期等等,这些内容后续补充。