第3章 Atlas整体架构:Bundle容器、动态部署层、组件化路由、资源与So管理
3.1 架构总览:分层边界与核心职责
Atlas 是阿里巴巴开源的组件化(Bundle)框架,其核心思想是将一个大型 App 拆分为多个独立的 Bundle,并在运行时实现动态部署。与 RePlugin 的“插件即独立 APK”不同,Atlas 更强调“组件化容器”,所有 Bundle 共享宿主进程空间,具备更紧密的耦合和更高的性能。
Atlas 架构分为四层,各层边界清晰,职责分离:
- 运行时容器层 (Bundle Container) — 提供 Bundle 的生命周期管理、ClassLoader 隔离、组件注册与安全校验。
- 动态部署层 (Dynamic Deployment Layer) — 负责 Bundle 的下载、安装、更新、卸载,以及版本管理和灰度策略。
- 组件化路由层 (Component Router) — 实现跨 Bundle 的 Activity、Fragment、Service、Provider 等组件的显式/隐式路由跳转。
- 资源与 So 管理层 (Resource & So Manager) — 统一管理 Bundle 内的资源文件(布局、图片、字符串等)和 Native 库的加载与合并。
以下结构图展示了 Atlas 的四层架构及数据流传递路径:
3.2 模块边界与系统分层
| 分层名称 | 模块边界 | 组件职责 | 对外依赖 |
|---|---|---|---|
| Bundle 容器 | 只管理 Bundle 的生命周期与 ClassLoader,不处理网络、IO、路由 | 安装/卸载/激活 Bundle、隔离 ClassLoader 命名空间、提供安全校验 | 依赖宿主 Application Context、系统 ClassLoader |
| 动态部署层 | 只处理 Bundle 的获取与更新,不感知具体组件 | 下载、校验、合并 patch、灰度下发、版本回退 | 依赖网络库(OkHttp/URLConnection)、文件系统、数据库(版本信息) |
| 组件化路由 | 只处理路由注册、查找、跳转,不涉及资源/So 加载 | 维护路由表、拦截器、参数解析、降级/兜底策略 | 依赖宿主路由表、字节码生成辅助 |
| 资源与 So 管理 | 只处理资源对象和 Native 库的合并、加载、缓存 | 资源包合并、ID 冲突解决、So 解压到宿主加载路径 | 依赖 AssetManager、System.loadLibrary |
3.3 组件职责详解
3.3.1 Bundle 容器
容器层是 Atlas 的核心入口,它维护着 BundleManager 单例,提供 installBundle()、activeBundle()、deactiveBundle() 等接口。每个 Bundle 对应一个独立的 BundleClassLoader,该 ClassLoader 的 parent 为宿主 ClassLoader,但彼此之间不可见,以此实现代码隔离;而资源隔离通过合并式 AssetManager 实现(见 3.3.4)。
3.3.2 动态部署层
采用“宿主 + patch”双层版本管理策略。每次部署发布时,Atlas 生成 Base 包与 Patch 包;客户端部署层先从服务端拉取 DiffManifest,基于宿主已安装版本计算增量,只下载变更的 Bundle。以下为典型部署流程:
- 轮询/推送 — 客户端向部署服务器查询可用更新。
- DiffManifest 下载 — 获取增量包清单和签名。
- 增量 Bundle 下载 — 只下载差异部分,宿主本地合成完整包。
- 签名校验 — 校验文件摘要与签名,防止篡改。
- 容器层安装 — 将 Bundle 放入安装目录,解析组件注册表。
3.3.3 组件化路由层
Atlas 的路由并非完全动态,而是采用“编译期注解 + 运行时查表”模式。通过 APT 在编译期生成 RouterMapping 类,记录每个 Activity/Fragment 与目标 Bundle 的映射;运行时路由层查询该表,按目标 Bundle 是否激活做跳转或降级。
// 编译期生成的 RouterMapping 代码示例(简化)
public final class RouterMapping {
public static final String ACTIVITY_LOGIN = "com.shop.bundle.user.LoginActivity";
public static final String BUNDLE_LOGIN = "bundle_user";
// ...
}
// 运行时路由调用
Router.with(context)
.url("shop://user/login")
.param("from", "main")
.start();
路由层内置“降级兜底”:若目标 Bundle 未安装或版本不匹配,可统一跳转到“下载中”或“错误提示”页面。
3.3.4 资源与 So 管理层
Atlas 的资源管理采用“合并式 AssetManager”,即在宿主进程创建一个“合并后的”全局 AssetManager,将所有 Bundle 的 assets 和 res 合并进来。资源冲突通过编译期分配的固定资源 ID 段解决——每个 Bundle 在 Gradle 插件中声明 bundleId,资源 ID 前缀与其绑定。
// build.gradle 中配置 Bundle ID
atlas {
bundleConfig {
bundleId = 10 // 分配给 bundle_user 的资源 ID 段 0x0Axxxxxx
}
}
So 文件管理则通过“解压 + 合并”策略:所有 Bundle 中的 .so 在安装时被解压到宿主私有目录,运行时通过 System.load() 指定路径,避免 System.loadLibrary 名称冲突。同时 Atlas 支持 SoMerge 模式,将多个 Bundle 的同名 .so 按优先级合并为一个。
3.4 数据流与控制流
以“用户点击跳转到另一个 Bundle 的 Activity”为例,完整的数据/控制流如下:
- 用户操作 — 宿主 Activity 发起路由调用(
Router.with(context).url("shop://detail/item?itemId=123").start())。 - 路由层解析 — 路由层查表,判断目标 Activity 属于
bundle_detail。 - 容器层查询 — 路由层调用容器层
BundleManager.isActive("bundle_detail"),若未激活则先调用activeBundle()。 - 容器层激活 — 容器层从指定目录加载 Bundle 的 dex 和资源(若未加载过),创建
BundleClassLoader,合并资源到全局 AssetManager。 - 路由跳转 — 路由层通过反射或 Intent.setClassName 设置目标 Activity 的完整类名(如
com.shop.detail.DetailActivity),由系统 Intent 机制完成跳转。 - So 加载(可选) — 若目标 Activity 依赖 Native 库,资源管理层在 Activity.onCreate 前解压对应的 .so 并调用
System.load()。
3.5 选型权衡与风险点
| 维度 | Atlas 方案 | 与 RePlugin 对比 |
|---|---|---|
| 代码隔离程度 | BundleClassLoader 隔离,但共享宿主堆内存 | RePlugin 插件 ClassLoader 完全独立,更严格 |
| 资源管理 | 合并式 AssetManager,ID 冲突风险较高 | 插件自身 AssetManager,无冲突 |
| 部署效率 | 支持增量 patch,适合频繁更新 | 全量插件包,更新粒度较粗 |
| 编译期侵入 | 需要 Gradle 插件定制(bundleId、资源段) | 几乎零侵入,插件编译独立 |
| 学习成本 | 高(需理解容器、部署、路由、资源管理四层) | 低(插件即 APK) |
3.6 落地步骤与验收标准
落地步骤
- 架构决策 — 评估业务场景是否适合“组件化 + 动态部署”模式,确定使用细粒度 Bundle 还是粗粒度 Bundle。
- Gradle 插件集成 — 在宿主工程中引入 Atlas Gradle 插件,配置 BundleConfig(bundleId、依赖关系)。
- 容器初始化 — 在 Application.onCreate 中调用
Atlas.getInstance().start(),配置安装目录和 ClassLoader 策略。 - 部署层搭建 — 实现部署服务器 API 接口,客户端封装下载/校验逻辑。
- 路由接入 — 添加 APT 注解处理器,生成路由表,编写路由拦截器。
- 资源与 So 验证 — 启动 CI 任务,对所有 Bundle 的资源 ID 段做冲突检测;交叉编译不同 .so 架构并验证加载。
- 灰度发布测试 — 在测试环境中使用少量设备推送 Bundle 更新,验证全链路(下载 → 校验 → 容器激活 → 路由跳转 → 资源展示 → So 调用)。
验收标准
- 所有 Bundle 能在宿主进程内独立安装、激活、去激活,无 ClassNotFoundException 或 NoSuchMethodError。
- 资源合并后,所有 Bundle 的界面显示正确,无资源错位或 ID 冲突异常。
- So 加载接口回调正常,Native 函数调用无 UnsatisfiedLinkError。
- 路由跳转在目标 Bundle 未激活时能自动触发容器激活,且耗时不超过 500ms(冷启动首次除外)。
- 增量部署场景下,客户端只下载差异数据,全量 diff 比率低于 20%。