插件代码已经可以加载了,但如果直接:
1 | startActivity(new Intent(this, PluginActivity.class)); |
系统还是会报错。
原因很简单:PluginActivity 没有在宿主 Manifest 里注册。
所以我们换个思路。
1. 注册一个 ProxyActivity
宿主 Manifest 里只注册:
1 | <activity |
插件里的 Activity 不需要注册给系统。
启动插件时,把原来的 Intent 包起来:
1 | 原始 Intent |
例如:
1 | Intent intent = new Intent(context, ProxyActivity.class); |
系统看到的是 ProxyActivity,所以不会因为找不到 PluginActivity 而失败。
2. ProxyActivity 做什么
ProxyActivity 启动以后,从 Intent 取出真正的类名:
1 | String className = getIntent().getStringExtra("plugin_class"); |
然后创建对象:
1 | Object plugin = clazz.getDeclaredConstructor().newInstance(); |
问题来了。
普通 Java 对象当然不等于 Android Activity。
所以这里不能简单地:
1 | PluginActivity activity = new PluginActivity(); |
然后就指望它自己拥有 Context、Window、Application 等东西。
3. 为什么教学版可以自己模拟生命周期
为了把原理讲清楚,我们可以定义一个插件接口:
1 | public interface PluginActivity { |
插件页面实现这个接口:
1 | public class MainPluginActivity implements PluginActivity { |
这样 ProxyActivity 只是一个宿主容器。
4. 真正的 Activity 兼容方案
如果要求插件里的类本身就是 Activity,那事情会复杂很多。
常见做法是让 ProxyActivity 继承 Activity,然后通过反射/接口方式把插件 Activity 的生命周期转过去。
但是这里有个本质问题:Android Activity 内部有大量系统状态,包括:
- Window;
- ActivityClientRecord;
- Token;
- FragmentController;
- Configuration;
- Result;
- Permissions;
- SavedState。
所以一个真正“透明”的 Activity 插件化框架,不可能只是几百行代码解决所有事情。
我们这个系列为了保持轻量,会选择:
插件页面实现框架定义的 PluginActivity 接口,宿主 ProxyActivity 负责真正的系统 Activity 身份。
这样代码少很多,而且不需要硬碰 Android 内部 Activity 实现。
5. 页面显示
例如插件页面最终返回一个 View:
1 | public interface PluginActivity { |
ProxyActivity:
1 |
|
这就已经能跑起来了。
6. 这种设计有什么缺点
缺点也很明显:插件不能完全把自己当成普通 Activity。
但是从学习角度看,我反而觉得这是一个好事情。
因为我们可以非常清楚地看到:
1 | Android系统只认识 ProxyActivity |
这就是 Activity 插件化最核心的思想。
后面的 Service、Receiver、Provider,也都会围绕这个思想展开,只是代理方式不同。