Android 插件化开发:代理 Activity 模式详解
在 Android 插件化架构中,通过 ClassLoader 加载外部的 Dex 或 Apk 文件,可以加载一些本地 APP 不存在的类,从而执行一些新的代码逻辑。但是使用这种方法却不能直接启动插件里的 Activity。
启动插件中 Activity 的两个主要问题
Activity 等组件是需要在 Manifest 中注册后才能以标准 Intent 的方式启动的。通过 ClassLoader 加载并实例化的 Activity 实例只是一个普通的 Java 对象,能调用对象的方法,但是它没有生命周期。而且 Activity 等系统组件是需要 Android 的上下文环境的(Context 等资源),没有这些东西 Activity 根本无法工作。
使用插件 APK 里的 Activity 需要解决两个核心问题:
- 如何使插件 APK 里的 Activity 具有生命周期:系统调用的生命周期方法必须被正确触发。
- 如何使插件 APK 里的 Activity 具有上下文环境:包括资源访问、Theme 应用以及 Context 依赖。
代理 Activity 模式为解决这两个问题提供了一种思路。
代理 Activity 模式原理
这种模式的特点是:主项目 APK 注册一个代理 Activity(命名为 ProxyActivity)。ProxyActivity 是一个标准的 Activity,但只是一个空壳,自身并没有什么业务逻辑。每次打开插件 APK 里的某一个 Activity 的时候,都是在主项目里使用标准的方式启动 ProxyActivity,再在 ProxyActivity 的生命周期里同步调用插件中的 Activity 实例的生命周期方法,从而执行插件 APK 的业务逻辑。
ProxyActivity + 没注册的 Activity = 标准的 Activity
第二个问题(上下文环境)通常通过动态替换 Context 实现,下面我们来重点分析使插件 APK 里的 Activity 具有生命周期的问题。
处理插件 Activity 的生命周期
目前真的没什么办法能够直接处理这个问题。一个 Activity 的启动,如果不采用标准的 Intent 方式,没有经历过 Android 系统 Framework 层级的一系列初始化和注册过程,它的生命周期方法是不会被系统调用的(除非你能够修改 Android 系统的一些代码,而这已经是另一个领域的话题了,这里不展开)。
那我们可以在主项目里创建一个 ProxyActivity,再由它去代理调用插件 Activity 的生命周期方法(这也是代理模式叫法的由来)。用 ProxyActivity(一个标准的 Activity 实例)的生命周期同步控制插件 Activity(普通类的实例)的生命周期。同步的方式主要有两种:
- 在 ProxyActivity 生命周期里用反射调用插件 Activity 相应生命周期的方法,简单粗暴。
- 把插件 Activity 的生命周期抽象成接口,在 ProxyActivity 的生命周期里调用。另外,多了这一层接口,也方便主项目控制插件 Activity。
方案一:用反射调用插件 Activity 相应生命周期
我们要在代理 activity 中去反射 apk 中 activity 的所有生命周期的方法,然后将 activity 的生命周期和代理 activity 的生命周期进行同步。首先,反射 activity 生命周期的所有方法,还反射了 onActivityResult 这个方法,尽管它不是典型的生命周期方法,但是它很有用。
protected void instantiateLifecircleMethods(Class<?> localClass) {
String[] methodNames = new String[] {
"onRestart",
"onStart",
"onResume",
"onPause",
"onStop",
};
(String methodName : methodNames) {
;
{
method = localClass.getDeclaredMethod(methodName, [] { });
method.setAccessible();
} (NoSuchMethodException e) {
e.printStackTrace();
}
mActivityLifecircleMethods.put(methodName, method);
}
;
{
onCreate = localClass.getDeclaredMethod(, [] { Bundle.class });
onCreate.setAccessible();
} (NoSuchMethodException e) {
e.printStackTrace();
}
mActivityLifecircleMethods.put(, onCreate);
;
{
onActivityResult = localClass.getDeclaredMethod(,
[] { .class, .class, Intent.class });
onActivityResult.setAccessible();
} (NoSuchMethodException e) {
e.printStackTrace();
}
mActivityLifecircleMethods.put(, onActivityResult);
}


