Android插件化动态加载原理(三)
Android插件化动态加载原理(三)
chunquedong 发表于1年前
Android插件化动态加载原理(三)
  • 发表于 1年前
  • 阅读 493
  • 收藏 3
  • 点赞 2
  • 评论 0

腾讯云 学生专属云服务套餐 10元起购>>>   

摘要: 本篇介绍如何启动插件中未注册的Activity

前面的文章介绍了动态加载apk包中的代码,本篇介绍如何启动插件中的未注册的Activity。Android系统要求所有的组件都在AndroidManifest.xml中注册。这样比较麻烦,插件中不能增加宿主中没有声明的Activity,很不灵活。要绕过Activity注册的限制也不是没有可能,主要有两种方法。

方案1 代理Activity

能想到的一种简单办法是代理模式,注册一个ProxyActivity,假设插件中未注册的Activity叫做PluginActivty。我们把PluginActivty当做普通的类,手动来维护Activity的生命周期。代码大概像这样:

class ProxyActivity extends Activity {
  Activty pluginActivity;
  @Overrride void onCreate() {
    pluginActivity = (Activity)Class.forName("com.test.PluginActivity").newInstance();
    Reflection.set(pluginActivity, "mBase", this);
    pluginActivity.onCreate();  
  }
  @Overrride void onDestroy() {
    pluginActivity.onDestroy();  
  }
  @Overrride void onTouch(Evnet e) {
    pluginActivity.onTouch(e);
  }
}

在ProxyActivity中,我们使用反射的方法初始化插件中未注册的Activity,并初始化一些属性。然后把系统告诉ProxyActivity的告诉PluginActivity。

这种代理方式有些缺点。有时候不能做到对开发者透明,要遵守一些编码约定。由于不是真正的Activity,有些特殊用法也会导致bug。所以实际上axbase插件框架使用的是第二种方案。

方案2 替换Instrumentation

另外一种方法是hook系统的Instrumentation类。Instrumentation类就像是Android系统天生为Android插件化定制的一样,恰到好处。

通过阅读Android源码ActivityThread.java可知,在每次打开新Activity时,由Instrumentation.newActivity方法负责创建Activity实例。如果我们能重写newActivity方法,我们注册一个叫ActivityStub的Activity组件,在newActivity中判断如果是要创建ActivityStub时创建一个插件的Activity返回给系统。这样达到了挂羊头卖狗肉的效果,成功的绕过了Activity需要注册的限制。代码大概像这样:

class MyInstrumentation extends Instrumentation {
  @Overrride Activity newActivity(ClassLoader cl, String className, Intent intent) {
    if (className.equals(ActivityStub.class.getName())) {
        pluginClassLoader.loadClass("com.test.PluginActivity");
        return (Activity)clazz.newInstance();
    }
    return super.newActivity(cl, className, intent);
  }
}

上面代码中的"com.test.PluginActivity"是写死的一个类名,如果要加载某个其它Activity则可以通过Intent.putExtra传递类名参数。

最后,我们再来看一下如何通过反射来替换掉系统的Instrumentation:

Context contextImpl = ((ContextWrapper) context).getBaseContext();
		Object activityThread = Reflection.getField(contextImpl, "mMainThread");
Reflection.setField(activityThread, "mInstrumentation", instrumentation);

同样是反射修改mMainThread的mInstrumentation为我们自定义的instrumentation对象。

方案3 ClassLoader欺骗

其实还有另外一种方式,就是在ClassLoader中,当请求加载ActivityStub类时,直接返回PluginActivity类,来欺骗系统。这种方式和instrumentation方法类似,只是没有Intent来传递类名参数,可能只能使用静态变量来传递。这里不再详细介绍。

其它关于插件化开发的内容在下篇介绍。 请关注axbasePlugin插件化开发框架Github

共有 人打赏支持
chunquedong
粉丝 47
博文 113
码字总数 33528
作品 4
×
chunquedong
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: