Android 轻量插件化:Activity 代理到底是怎么实现的

插件代码已经可以加载了,但如果直接:

1
startActivity(new Intent(this, PluginActivity.class));

系统还是会报错。

原因很简单:PluginActivity 没有在宿主 Manifest 里注册。

所以我们换个思路。

1. 注册一个 ProxyActivity

宿主 Manifest 里只注册:

1
2
3
<activity
android:name=".plugin.ProxyActivity"
android:exported="false" />

插件里的 Activity 不需要注册给系统。

启动插件时,把原来的 Intent 包起来:

1
2
3
4
5
原始 Intent

PluginActivity

ProxyActivity

例如:

1
2
3
Intent intent = new Intent(context, ProxyActivity.class);
intent.putExtra("plugin_class", "com.demo.plugin.PluginActivity");
context.startActivity(intent);

系统看到的是 ProxyActivity,所以不会因为找不到 PluginActivity 而失败。

2. ProxyActivity 做什么

ProxyActivity 启动以后,从 Intent 取出真正的类名:

1
2
3
4
String className = getIntent().getStringExtra("plugin_class");
Class<?> clazz = pluginManager
.getClassLoader()
.loadClass(className);

然后创建对象:

1
Object plugin = clazz.getDeclaredConstructor().newInstance();

问题来了。

普通 Java 对象当然不等于 Android Activity。

所以这里不能简单地:

1
PluginActivity activity = new PluginActivity();

然后就指望它自己拥有 Context、Window、Application 等东西。

3. 为什么教学版可以自己模拟生命周期

为了把原理讲清楚,我们可以定义一个插件接口:

1
2
3
4
5
6
7
8
public interface PluginActivity {
void onCreate(PluginContext context, Bundle state);
void onStart();
void onResume();
void onPause();
void onStop();
void onDestroy();
}

插件页面实现这个接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MainPluginActivity implements PluginActivity {

@Override
public void onCreate(PluginContext context, Bundle state) {
// 创建页面
}

@Override
public void onStart() {}

@Override
public void onResume() {}

@Override
public void onPause() {}

@Override
public void onStop() {}

@Override
public void onDestroy() {}
}

这样 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
2
3
public interface PluginActivity {
View onCreateView(PluginContext context, Bundle state);
}

ProxyActivity:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

String className = getIntent()
.getStringExtra("plugin_class");

PluginActivity activity = createPluginActivity(className);

View view = activity.onCreateView(
new PluginContext(this, pluginManager),
savedInstanceState
);

setContentView(view);
}

这就已经能跑起来了。

6. 这种设计有什么缺点

缺点也很明显:插件不能完全把自己当成普通 Activity。

但是从学习角度看,我反而觉得这是一个好事情。

因为我们可以非常清楚地看到:

1
2
3
4
5
Android系统只认识 ProxyActivity

ProxyActivity 找到插件类

PluginActivity 接管页面逻辑

这就是 Activity 插件化最核心的思想。

后面的 Service、Receiver、Provider,也都会围绕这个思想展开,只是代理方式不同。

坚持原创技术分享,您的支持将鼓励我继续创作!