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通过预埋的“坑位”与“插件管理通道”进行交互,动态加载层负责将插件字节码与资源无缝注入到宿主进程中。

RePlugin四层架构与数据流 RePlugin 四层架构与关键数据流 宿主应用层 Application · 坑位Activity栈 · 进程管理 · 全局Context 插件管理器 安装/卸载/更新 · 版本兼容检查 · 权限校验 · 组件调度 动态加载层 PluginClassLoader · 资源合并 · Dex加载 · Native库注入 组件注册机制 Activity坑位复用 · Intent重定向 · Proxy子类 · 清单预注册 宿主APK 预注册坑位 · 插件管理通道 · 全局Context 插件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启动为例,说明四层之间的协作流程:

  1. 宿主应用层:用户点击入口按钮,调用 PluginManager.startActivity(intent)。
  2. 插件管理器:解析intent,查找插件APK是否已安装。若未安装,触发下载与安装流程。若已安装,取出插件ComponentInfo,并将intent的目标组件替换为宿主坑位Activity(如 PluginActivityProxy1)。
  3. 组件注册机制:系统启动坑位Activity,在坑位的onCreate中调用 ComponentDispatcher.inflatePluginView()。
  4. 动态加载层:PluginClassLoader加载插件Activity类,ResourceMerger合并插件布局资源,创建插件Activity实例并调用其onCreate。
  5. 反馈数据流:插件Activity通过宿主提供的全局Context(由宿主Application传入)进行Toast、startActivity等操作,所有系统调用仍经过宿主代理。

2.5 落地步骤与验收标准

2.5.1 落地步骤

  1. 宿主工程配置:在宿主模块的 build.gradle 中添加RePlugin Gradle插件及依赖,并在AndroidManifest中预注册至少5个Activity坑位、1个ProxyService以及ProxyProvider。
  2. 插件管理器初始化:在宿主Application.onCreate中调用 RePlugin.installPluginManager(),并设置全局异常处理。
  3. 动态加载层接入:检查宿主ClassLoader的dexElements结构,确保RePlugin的补丁Dex注入逻辑能正确执行。
  4. 组件注册验证:编写测试插件,分别包含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。