第4章 Atlas架构深度解析:Bundle概念、AXML资源合并、ClassLoader隔离、动态部署引擎
系统边界与分层架构
Atlas 的核心思路是将宿主应用拆解为若干可独立开发、编译和动态更新的 Bundle。系统边界定义在宿主 APK 与 Bundle APK 之间,通过三层架构实现解耦:
- 宿主层:提供运行容器、基础框架、公共资源与类库。宿主本身不包含业务逻辑,仅负责 Bundle 的生命周期管理。
- Bundle 层:每个 Bundle 是一个独立的业务模块,包含自身资源、代码、AndroidManifest 片段。Bundle 之间不可直接引用代码,仅通过接口或宿主提供的服务进行通信。
- 内核层:Atlas 运行时内核,负责启动过程、ClassLoader 组装、资源合并、动态部署信号的监听与执行。
选型权衡:Atlas 的 Bundle 粒度比 RePlugin 更细,适合超大型 App(如手机淘宝)的渐进式解耦,但初始配置成本更高。
信任链路:宿主与 Bundle 之间的信任通过签名校验建立。Atlas 要求每个 Bundle 必须使用与宿主相同的签名进行签名,否则拒绝加载。
Bundle 概念与生命周期
Bundle 是一个经过 Atlas 工具链加工的独立 APK,包含完整代码、资源及合并后的 AndroidManifest 片段。它既不是宿主的一部分,也不是完全独立的进程容器,而是运行在宿主进程内的一个“模块”。
| Bundle 状态 | 描述 | 触发时机 |
|---|---|---|
| 已安装 | Bundle APK 解压至宿主内部存储,资源表已注册 | 首次启动时或动态部署安装后 |
| 已加载 | ClassLoader 已创建并加入隔离链,可反射调用入口类 | 首次访问 Bundle 组件时 |
| 已挂载 | AXML 资源已合并至宿主 Resources | Bundle 被激活时 |
| 已卸载 | ClassLoader 废弃,资源表注销 | 动态部署更新老旧 Bundle 时 |
- 数据流:Bundle 安装 → 读取 AndroidManifest → 注册 Activity/Service → 等待第一次加载。
- 控制流:宿主启动 → Atlas 内核扫描已安装 Bundle → 按依赖顺序预加载基础 Bundle → 按需懒加载业务 Bundle。
AXML 资源合并机制
多个 Bundle 的资源 ID 存在冲突风险。Atlas 在编译期通过 AXML 资源合并 过程解决:将每个 Bundle 的 resources.arsc 反编译为中间 XML,然后与宿主的公共资源表进行合并,生成统一的资源映射表。
@工具侧:AtlasMergeRes
输入:宿主 R.txt + 各 Bundle 的 resources.arsc
输出:合并后的 resources.arsc 及插件 R.java
过程:
1. 收集所有 Bundle 中定义的资源名称和类型
2. 检测重复资源名(优先使用宿主版本)
3. 重新分配全局资源 ID
4. 生成最终的 resources.arsc
- 风险点:资源 ID 重分配可能导致旧版本 Bundle 引用错误的资源。必须在更新 Bundle 时同时发布新合并的资源表。
- 落地步骤:在 CI 流水线中配置
AtlasMergeRes任务,确保每次打包前执行。验收标准:宿主安装后,所有 Bundle 中引用的资源 ID 与宿主预期一致,不出现ResourceNotFoundException。
ClassLoader 隔离体系
Atlas 使用 多 ClassLoader 链 实现 Bundle 之间的代码隔离。每个 Bundle 拥有一个独立的 AtlasClassLoader,并形成父子委托链:
Boot ClassLoader
└─ PathClassLoader (宿主)
└─ AtlasClassLoader (BundleA)
└─ AtlasClassLoader (BundleB)
└─ AtlasClassLoader (BundleC, 依赖 BundleB)
- 信任链路:只有被宿主信任的 Bundle 才能被挂载到链中。信任声明在宿主
AndroidManifest的atlas-bundle配置中。 - 风险点:如果 Bundle A 和 Bundle B 包含同名类,委托链将总是优先返回先加载的版本。设计时应避免 Bundle 间依赖未声明的类。
- 验收标准:通过
Class.forName验证,Bundle 只能加载自身与宿主提供的类,无法访问其他 Bundle 的内部类。
选型权衡:ClassLoader 链会增加类查找开销。每增加一个 Bundle,首次类加载平均耗时增加 5-15ms。对冷启动敏感的场景需控制 Bundle 数量在 30 个以内。
动态部署引擎
Atlas 的动态部署引擎负责在不重启应用的前提下更新 Bundle。核心控制流如下:
- 信号接收:引擎后台轮询或接收推送通知,获取新 Bundle APK 的下载地址。
- 下载与验证:下载 APK 后校验签名与宿主一致,防止篡改。
- 替换与挂载:将旧 Bundle 标记为待卸载,新 Bundle 解压至临时目录,通过
AtlasClassLoader重新挂载,同时更新资源表。 - 热切换:等待已加载的旧组件全部销毁,然后更新组件注册表,使新请求指向新 Bundle。
数据流示例:动态部署更新一个 Bundle 时,受影响的 Activity 会经历 onDestroy() → 新 Bundle 挂载 → 下次 startActivity() 自动使用新组件。
落地步骤:
- 宿主集成 Atlas SDK 并开启动态部署监听。
- 构建动态部署 Bundle,使用
atlas-patch工具生成差异包。 - 部署至线上测试环境,验证更新成功率 ≥99%,且不导致宿主崩溃。
选型权衡总结
| 维度 | Atlas | 权衡说明 |
|---|---|---|
| Bundle 粒度 | 极细(可到单个模块) | 适合大团队分布式开发,但初始化配置复杂 |
| 资源合并 | 编译期全量合并 | 避免运行时冲突,代价是打包时间显著增加 |
| ClassLoader 隔离 | 多链委托 | 强隔离性,但类加载性能下降,需注意 Bundle 数量 |
| 动态部署 | 支持全量 + 差分更新 | 差分算法优化后包体较小,但需要服务端配合处理基线版本 |
风险点与规避措施
- 资源 ID 错乱:强制在每次发布 Bundle 时执行资源合并流水线,禁止手工修改 R 文件。
- 类加载死锁:避免在 Bundle 初始化阶段进行跨 Bundle 类加载。可使用延迟初始化或在宿主线程之外处理。
- 动态部署回滚困难:每次动态更新前,保留当前 Bundle 的快照目录。一旦新版本崩溃,内核应自动回退到快照版本。
验收标准
- 宿主安装后,所有预置 Bundle 的 Activity 能成功启动并显示正确资源。
- 通过 ADB 命令或推送信号触发动态部署,新的 Bundle APK 生效后,旧组件页面正常关闭,新组件 100% 可用。
- ClassLoader 隔离验证:Bundle A 无法通过反射访问 Bundle B 的内部类,只能通过宿主声明接口调用。
- Monkey 测试 2 小时,无因资源冲突、ClassLoader 或动态部署导致的崩溃。