2. RePlugin整体架构:宿主与插件关系、插件管理器、动态加载层、组件注册机制
2.1 架构概览:四层模型与模块边界
RePlugin将整体系统划分为四层,每层职责清晰、边界固化,便于独立演进与问题隔离。下表列出各层核心职责及其对上层提供的服务契约。
| 层级 | 核心职责 | 对上暴露的关键接口 | 模块边界 |
|---|---|---|---|
| 宿主应用层 | 承载插件运行环境,提供全局Application、基础Activity栈、进程管理 | PluginManager.openActivity / startService | 不直接依赖具体插件Class |
| 插件管理器 | 生命周期调度、安装/卸载/更新、版本兼容、权限管控 | IPluginManager 接口族 | 隔离宿主与动态加载层 |
| 动态加载层 | ClassLoader 构建、资源合并、Dex 加载、Native库注入 | PluginClassLoader / ResourceBridge | 仅操作字节码与资源文件 |
| 组件注册机制 | 四大组件占位与动态代理、Intent 重定向、Activity坑位复用 | ComponentDispatcher / ActivityProxy | 不感知业务逻辑 |
架构关键决策:RePlugin 选择“宿主管生命周期,插件管业务逻辑”的分离模式。所有安卓组件(Activity、Service、BroadcastReceiver、ContentProvider)的注册与调度均由宿主侧的占位组件完成,插件业务代码不直接参与系统组件注册流程。
2.2 系统分层:内联SVG全貌图
下图展示了RePlugin的四层架构以及关键数据流与控制流路径。宿主APK与插件APK通过预埋的“坑位”与“插件管理通道”进行交互,动态加载层负责将插件字节码与资源无缝注入到宿主进程中。
2.3 组件职责详解
2.3.1 宿主应用层
宿主是唯一被系统PackageManager识别的APK。其核心职责是提供运行环境与组件调度入口。
- 坑位Activity:在宿主AndroidManifest.xml中预注册若干个通用Activity占位坑。每个插件Activity启动时,实际先启动坑位Activity,再由插件管理器动态替换为插件Activity实例。
- 全局Application:插件没有独立Application,插件初始化代码被合并到宿主Application回调中执行。
- 进程管理:宿主负责为插件分配进程(单进程/多进程策略),并维护进程间通信通道。
2.3.2 插件管理器
作为调度中枢,负责插件生命周期的全流程管理。关键职责如下:
| 功能模块 | 内外部接口 | 典型实现 |
|---|---|---|
| 插件安装 | PluginManager.install() | 通过ContentProvider或FileProvider将插件APK复制到私有目录,校验签名与版本。 |
| 组件调度 | PluginManager.startActivity() / startService() | 解析Intent中的ComponentName,查找插件内目标类,映射到宿主坑位。 |
| 版本兼容 | IVersionChecker | 基于宿主SDK版本号与插件声明的兼容版本范围进行匹配校验。 |
| 权限管控 | IPermissionValidator | 插件声明的权限需要与宿主权限白名单做交叉检查。 |
选型权衡:RePlugin的插件管理器采用“集中式调度”而非“分布式注册”。这降低了插件间耦合,但所有组件跳转必须经过管理器网关,极端场景下可能成为单点瓶颈。若对冷启动性能有极高要求,可考虑将频繁调用的插件接口做本地Cache映射。
2.3.3 动态加载层
动态加载层是RePlugin最底层的技术实现,承担将插件字节码与资源“注入”到宿主进程的任务。其关键组件如下:
- PluginClassLoader:自定义ClassLoader,采用双亲委派机制的变体。加载插件类时,优先从宿主ClassLoader查找公共依赖(如support库),再从插件自身Dex加载独有类。
- 资源合并器(ResourceMerger):将插件资源与宿主资源合并为一个全局资源表。RePlugin基于反射替换宿主PackageManager的Resources部分,使插件资源ID可直接在宿主代码中使用。
- DexInstaller:将插件Dex文件通过系统DexClassLoader加载,或通过OTA方式注入到宿主PathClassLoader的dexElements数组头部,实现类覆盖。
- NativeLoader:负责将插件自带的.so文件复制到宿主native目录,并通过System.load加载。
// PluginClassLoader 核心逻辑示意
public class PluginClassLoader extends ClassLoader {
private final ClassLoader hostLoader;
private final DexClassLoader pluginDexLoader;
@Override
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
// 1. 先尝试从宿主公共依赖查找
try {
return hostLoader.loadClass(name);
} catch (ClassNotFoundException e) {
// 2. 未命中时从插件Dex加载
return pluginDexLoader.loadClass(name);
}
}
}
风险点:动态加载层对Android API版本敏感。Android Q(API 29)开始,对ClassLoader的反射修改受到限制;Android R(API 30)进一步收紧了资源合并的权限。RePlugin在Android 11+ 设备上需要额外兼容处理,否则可能导致插件加载失败或资源错乱。
2.3.4 组件注册机制
RePlugin的组件注册机制围绕“坑位复用”与“动态代理”两个核心设计展开。
- Activity坑位池:宿主预注册固定数量的通用Activity(如
PluginActivityProxy1到PluginActivityProxyN)。每次启动插件Activity时,从池中分配一个空闲坑位,将Intent的component替换为坑位,插件管理器再通过Activity生命周期回调把业务视图插入坑位。 - Service动态绑定:插件Service通过宿主预注册的ServiceProxy进行转发。所有onStartCommand、onBind等回调在代理Service中触发,再委托给插件Service实例。
- BroadcastReceiver与ContentProvider:Receiver采用动态注册方式,插件安装时由插件管理器调用
registerReceiver;Provider则通过宿主预注册的ProxyProvider进行URI映射。
| 组件类型 | 注册方式 | 坑位策略 | 生命周期归属 |
|---|---|---|---|
| Activity | 静态预注册(宿主manifest) | 固定坑位池,分配复用 | 宿主管理onCreate/onDestroy |
| Service | 静态预注册(宿主manifest) | 通过ServiceProxy转发 | 宿主管理onStartCommand/onBind |
| BroadcastReceiver | 动态注册(插件安装时) | 无坑位,直接注册到宿主Context | 宿主管理onReceive |
| ContentProvider | 静态预注册 + URI映射 | 通过ProxyProvider转发 | 宿主管理query/insert/delete/update |
2.4 控制流与数据流
以一次典型的插件Activity启动为例,说明四层之间的协作流程:
- 宿主应用层:用户点击入口按钮,调用
PluginManager.startActivity(intent)。 - 插件管理器:解析intent,查找插件APK是否已安装。若未安装,触发下载与安装流程。若已安装,取出插件ComponentInfo,并将intent的目标组件替换为宿主坑位Activity(如
PluginActivityProxy1)。 - 组件注册机制:系统启动坑位Activity,在坑位的onCreate中调用
ComponentDispatcher.inflatePluginView()。 - 动态加载层:PluginClassLoader加载插件Activity类,ResourceMerger合并插件布局资源,创建插件Activity实例并调用其onCreate。
- 反馈数据流:插件Activity通过宿主提供的全局Context(由宿主Application传入)进行Toast、startActivity等操作,所有系统调用仍经过宿主代理。
2.5 落地步骤与验收标准
2.5.1 落地步骤
- 宿主工程配置:在宿主模块的
build.gradle中添加RePlugin Gradle插件及依赖,并在AndroidManifest中预注册至少5个Activity坑位、1个ProxyService以及ProxyProvider。 - 插件管理器初始化:在宿主Application.onCreate中调用
RePlugin.installPluginManager(),并设置全局异常处理。 - 动态加载层接入:检查宿主ClassLoader的dexElements结构,确保RePlugin的补丁Dex注入逻辑能正确执行。
- 组件注册验证:编写测试插件,分别包含Activity、Service、Receiver、Provider四种组件,逐一验证能否正常启动与回调。
2.5.2 验收标准
| 验收项 | 预期结果 | 检测手段 |
|---|---|---|
| 插件Activity启动 | 插件Activity正常显示,生命周期与宿主坑位一致 | adb logcat 查看PluginManager日志 |
| 插件Service运行 | Service onStartCommand 被正确调用 | 测试插件内打印日志 |
| 插件Receiver广播 | 插件能接收宿主发出的有序广播 | 发送自定义广播并观察插件回调 |
| 插件ContentProvider | 宿主通过ProviderURI读写数据成功 | 单元测试query/insert |
| 动态加载层资源 | 插件布局中引用的宿主主题色生效,插件自有两张图片显示正确 | UI自动化截图对比 |
2.6 选型权衡与风险点清单
- 权衡:坑位数量 vs 内存占用。坑位Activity越多,并发启动的插件Activity上限越高,但每个坑位在Application启动时都会被系统创建一次Class实例,增加内存开销。作者建议:大型App(50+插件)选择8~12个坑位;小型App(5~10插件)选择4~6个坑位。
- 风险:Android Fragment系统兼容。插件中的Fragment若直接使用宿主Activity的FragmentManager,可能因ClassLoader不一致导致“Fragment not attached”异常。解决方式:插件内Fragment必须通过
pluginContext创建的FragmentManager来管理。 - 风险:多进程插件。RePlugin的多进程支持需要宿主的每个子进程都初始化一次插件管理器,且子进程的ClassLoader构造需要单独处理,否则跨进程组件调用会收到ClassNotFoundException。
架构师建议:在接入RePlugin前,务必先梳理宿主与插件之间的共享依赖版本。RePlugin的动态加载层无法容忍宿主与插件使用不同版本的公共库(如support-v4、AndroidX、OkHttp)。推荐使用统一依赖版本管理,并在插件构建时通过provided或compileOnly引入共享库,避免将公共库打包进插件APK。