Android 模块化架构演进 12 年:从单体到 Compose 的完整路径
从 2014 年的单体应用到 2026 年的 Compose Multiplatform,Android 架构经历了六次重大演进。每一次演进背后都是工程痛点驱动的架构决策——不是技术追新,而是被规模逼出来的。本文以一个百万日活 App 的演进史为蓝本,给出每一步的 trade-off 与落地路径。
一、为什么 Android 模块化值得讲 12 年
2014 年到 2026 年,Android 应用架构经历了六次重大演进。这 12 年是中国移动互联网最疯狂的年代——App 从工具变成平台、DAU 从万到百万到亿、研发团队从几人到几百人。每一次架构演进都是被规模逼出来的:单体撑不住就拆,组件化撑不住就插件化,XML 撑不住就 Compose。
这条演进路径的普适性极强——任何大型前端、后端架构的演进,本质都是同一套思路:单体 → 模块化 → 组件化 → 平台化。理解 Android 12 年演进,就理解了大规模工程架构的核心矛盾。
graph LR
A[单体 App
2014] --> B[MultiDex
2015]
B --> C[组件化
2017]
C --> D[插件化
2019]
D --> E[Compose
2022]
E --> F[KMP/多端
2026]
二、第一阶段:单体 App 与 MultiDex
2014 年,绝大多数 App 还是单体结构——一个 Application、几千个 Activity、几万行 Java。随着 App 膨胀,第一个出现的问题不是性能,而是方法数超限。Android Dalvik 单 dex 方法数限制 65536,超出后无法安装。
2.1 MultiDex 救火
MultiDex 是 Android 5.0 之前的官方救火方案——把方法拆分到多个 dex 文件。原理简单但坑极多:启动时间增加(需要加载多个 dex)、ANR 风险(首次启动慢)、兼容性问题(不同 ROM 实现不同)。我们当时用了 Google 官方 multidex 库,App 启动时间从 800ms 涨到 2.5s。
graph TB
A[App] --> B[主 dex
启动必需类]
A --> C[副 dex 1
业务类]
A --> D[副 dex 2
业务类]
B -.启动加载.-> E[ClassLoader]
C -.延迟加载.-> E
D -.延迟加载.-> E
2.2 痛点暴露
MultiDex 解决了方法数问题,但暴露了更深的架构问题:所有类在一个项目里,任何改动都触发全量编译,编译 5 分钟起步;业务代码混杂,支付逻辑和首页 UI 在同一个包;团队协作混乱,10 个工程师改同一份代码天天冲突。
三、第二阶段:组件化(业务模块拆分)
2017 年我们开始组件化——把单体 App 拆成多个 Gradle module,每个 module 对应一个业务线。这是 12 年里**最重要的一次架构演进**,奠定了后续所有演进的基础。
3.1 拆分原则
组件化的核心是模块边界——按业务线拆,不是按技术层拆。我们定义了四种模块:
- App module:壳工程,负责 module 装配和路由
- 业务 module:按业务线划分(首页、订单、个人中心)
- 功能 module:跨业务的通用功能(登录、支付、推送)
- 基础 module:基础设施(网络、数据库、UI 组件)
graph TB
A[App Module
壳] --> B[首页]
A --> C[订单]
A --> D[个人中心]
B --> E[登录]
C --> E
C --> F[支付]
D --> E
E --> G[基础-网络]
F --> G
所有 module --> H[基础-公共]
3.2 独立调试 vs 集成调试
组件化后每个 module 都要能独立运行(单独调试)和集成运行(作为整体)。Gradle 的 apply plugin 切换实现这个能力——开发模式下 module 是独立 Application,发布模式下是 Library。这是组件化的工程关键,每个 module 必须做。
3.3 trade-off
组件化的代价是短期效率下降——模块拆分、路由设计、独立调试配置,前 3 个月效率降低 30%。但收益是长期效率指数级提升——3 年后构建时间从 5 分钟降到 30 秒,团队可以并行开发不冲突。短期痛苦换长期收益,这是架构演进的本质。
四、第三阶段:插件化与按需加载
组件化解决了代码组织问题,但没解决包大小问题。到 2019 年,我们的 App 已经膨胀到 80MB——继续增长会被应用商店警告,下载转化率也会跌。插件化应运而生。
4.1 插件化原理
插件化把"加载"和"运行"解耦——宿主 App 启动时只加载核心模块,业务模块以插件包形式下发,运行时动态加载。原理是动态类加载 + 资源替换 + 四大组件 hack。我们用了业界成熟的 Shadow / RePlugin 方案,避免从零造轮子。
graph LR
A[用户启动 App] --> B[宿主加载]
B --> C{网络下发插件}
C -->|下载| D[本地存储]
D --> E[动态加载]
E --> F[运行业务模块]
4.2 收益与代价
插件化的核心收益:包大小降低 50%+(核心 30MB + 插件按需下载)、热修复能力(绕过应用商店审核)、业务并行发布(各业务独立发版)。代价:运行时性能损耗 5-15%(类加载开销)、兼容性地狱(每个 Android 版本都有 hack 兼容)。
4.3 退潮
2021 年后插件化开始退潮。原因有三:Android 12 起对动态类加载限制收紧、Google Play 政策收紧、App Bundle(AAB)的出现让"按需下载"原生支持。我们最终把插件化重构为按需 module + App Bundle,实现官方原生方案。
五、通信机制:Intent → AIDL → Arouter → Hilt
模块化后最关键的工程问题是模块间通信。每个模块不能直接依赖其他模块,必须通过路由/接口通信。我们经历了四代方案。
5.1 Intent 系统级
Android 原生的 Activity 跳转机制,跨进程也能用。优点是系统原生、稳定;缺点是字符串拼接易错、无法传递复杂对象、跳转逻辑分散。早期我们用 Intent,路由跳转散落各处,维护噩梦。
5.2 Arouter 编译期路由
2018 年我们引入 Arouter——编译期生成路由表,跳转变成 ARouter.getInstance().build("/order/detail").navigation()。好处:字符串集中管理、编译期校验、支持拦截器(登录拦截、埋点)。Arouter 是组件化时代的标配。
5.3 Hilt 依赖注入
路由解决"跳转",但模块间服务调用(获取用户信息、调用支付服务)还需要解耦。我们用 Hilt(基于 Dagger)做依赖注入——接口和实现分离,模块间通过接口通信而非具体实现。这是 Google 官方推荐方案,编译期生成代码,运行时零反射。
graph LR
A[业务模块] -->|@Inject| B[接口]
B -.实现由 Hilt 注入.-> C[实现模块]
A -.不直接依赖.-> C
5.4 通信机制选型矩阵
| 方案 | 适用场景 | trade-off |
|---|---|---|
| Intent | 页面跳转、跨进程 | 原生稳定 vs 字符串脆弱 |
| Arouter | 模块内页面跳转 | 编译期校验 vs 额外依赖 |
| Hilt | 服务调用、依赖解耦 | 类型安全 vs 学习成本 |
| EventBus | 跨模块事件 | 解耦 vs 调试困难 |
六、Compose 时代:声明式 UI 重塑架构
2021 年 Google 发布 Compose 1.0,这是 Android UI 的范式转移——从命令式(XML + findViewById)到声明式(@Composable)。它不只是 UI 框架变化,而是整个架构的重塑。
6.1 范式转移的本质
XML + View 的命令式 UI 需要手动管理状态——view.setText()、view.setVisibility(),状态分散在各处。Compose 的声明式 UI 状态驱动 UI——state 变化自动 recompose,开发者只描述 UI 应该长什么样。这是 React 2013 年的范式,Android 晚了 8 年但终于到来。
graph LR
A[命令式
手动 setText] --> B[状态不一致]
B --> C[bug]
D[声明式
state 自动驱动] --> E[单一数据源]
E --> F[UI 永远一致]
6.2 单向数据流
Compose 推荐单向数据流(UDF)——State 由 ViewModel 持有,UI 只读取 State + 发送 Event。这是 MVVM 的强化版,强制数据流单向,杜绝双向绑定带来的混乱。我们重构后的架构:ViewModel + StateFlow + Compose,比旧的 MVP/MVVM 清晰一个数量级。
6.3 迁移路径
不能一次重写,我们做了渐进式迁移——新页面用 Compose,旧页面用 XML,通过 ComposeView 嵌入。2 年时间把 80% 页面迁移到 Compose,构建时间反而下降 30%(Compose 编译期优化更强)。最终目标是 2026 年全面 Compose 化,移除 XML 依赖。
七、构建优化:增量编译与 R8
大型 App 的构建时间从 30 分钟到 30 秒,是研发效率的生命线。我们做了四层优化。
7.1 Gradle 构建缓存
Gradle 内置本地缓存 + 远程缓存。本地缓存复用单机历史构建结果,远程缓存复用团队构建结果。我们搭建了公司级 Gradle Remote Cache,缓存命中率 60%,平均构建时间下降 50%。
7.2 增量编译与 KSP
Kotlin 编译器从 KAPT 迁移到 KSP(Kotlin Symbol Processing)——编译速度提升 2-3 倍。KSP 是 Google 推荐的注解处理方案,比 KAPT 快很多。我们迁移后增量编译时间从 8s 降到 2s。
7.3 R8 代码混淆
R8 替代 ProGuard 做代码混淆、压缩、优化。开启 R8 full mode 后,包大小降低 25%、方法数减少 30%、运行时性能提升 10%。代价是混淆规则要写好,否则会出运行时崩溃。
graph LR
A[源码] --> B[Kotlin 编译]
B --> C[R8 优化]
C --> D[DEX 输出]
E[构建缓存] -.命中.-> D
7.4 模块并行构建
组件化最大的构建收益——模块并行编译。单体时所有代码线性编译,组件化后 Gradle 并行调度独立 module 构建。我们 60 个 module 全量构建时间从 8 分钟降到 1.5 分钟。
| 优化手段 | 效果 | 代价 |
|---|---|---|
| Remote Cache | 构建时间 -50% | 服务端部署 |
| KSP 替代 KAPT | 编译速度 2-3x | 注解处理器适配 |
| R8 full mode | 包大小 -25% | 混淆规则维护 |
| 模块并行 | 构建时间 -80% | 模块拆分成本 |
八、测试架构:四层金字塔
大型 App 必须有完整的测试架构。我们用四层金字塔——单元测试 → 集成测试 → UI 测试 → 端到端测试——每层有不同的目标、不同的工具、不同的覆盖率指标。
8.1 单元测试(70%)
JUnit + Mockito + Turbine(Flow 测试)。单元测试覆盖业务逻辑、工具类、ViewModel。目标覆盖率:业务代码 80%、基础库 90%。模块化后单元测试可以跨模块隔离运行,无需启动 Android。
8.2 集成测试(20%)
Robolectric + 模拟数据库/网络。集成测试验证模块内多个组件协作。这部分测试启动快、覆盖中等粒度逻辑。
8.3 UI 测试(8%)
Compose Test / Espresso。UI 测试验证关键页面渲染和交互。目标覆盖:核心页面 100%、其他页面按优先级。
8.4 端到端测试(2%)
Appium / Firebase Test Lab。E2E 测试覆盖核心业务流程(登录、下单、支付)。这部分测试慢且脆弱,必须严格控制数量。
graph TB
A[单元 70%
快便宜]
B[集成 20%
中等]
C[UI 8%
慢]
D[E2E 2%
慢且脆弱]
A --> B --> C --> D
九、团队协作:模块 Owner 与 SemVer
模块化是技术问题,更是团队协作问题。100 个工程师怎么高效协作?我们靠三个机制:模块 Owner、SemVer、Code Review。
9.1 模块 Owner
每个核心 module 指定2-3 个 Owner,负责模块的整体质量:架构决策、依赖审批、Code Owner Review。新人改这个模块必须先得到 Owner 同意。这避免了"谁都改、改得乱"的状态。
9.2 SemVer 版本管理
跨模块依赖用 Semantic Versioning(SemVer):MAJOR.MINOR.PATCH。破坏性改动必须升级 MAJOR 版本,迫使所有依赖方主动适配。我们用 versionCode + api 双重管理,二进制兼容性检测自动化。
9.3 Code Review 与 CI
所有 PR 必须 2 个 Owner Review 通过 才合并。CI 强制跑:单元测试 + lint + 构建检查。我们用 GitHub Actions + 自研 Pipeline,平均 PR review 时间 4 小时,紧急 PR 1 小时内处理。
graph LR
A[PR 提交] --> B[CI 检查]
B -->|通过| C[Owner Review]
C -->|2人通过| D[合并]
C -->|需修改| A
十、未来形态:Compose Multiplatform 与 KMP
展望未来 5 年,Android 架构演进有三大方向:Compose Multiplatform(跨平台 UI)、Kotlin Multiplatform(共享业务逻辑)、AI 辅助开发。
10.1 Compose Multiplatform
JetBrains 在 2024 年发布 Compose Multiplatform 稳定版——同一套 Compose 代码同时跑在 Android、iOS、Desktop、Web。这是Android 架构的下一站——UI 不再平台绑定。我们已经在新业务试点,预计 2027 年成为主流。
10.2 Kotlin Multiplatform(KMP)
KMP 让 Kotlin 代码跨平台编译,共享业务逻辑(数据层、网络层、业务规则),UI 层各自平台原生。配合 Compose Multiplatform 就是完整跨平台方案。
10.3 AI 辅助开发
AI 编码助手(Copilot、通义灵码)已经在改变 Android 开发——自动生成 Compose 代码、自动写单元测试、自动做 Code Review。我们内部在试点用 LLM 自动生成 UI 预览代码 和测试用例,效率提升 40%。
graph LR
A[Android 源码] --> B[KMP]
B --> C[iOS]
B --> D[Desktop]
B --> E[Web]
F[Compose Multiplatform] --> C
F --> D
F --> E
📌 关键架构决策清单
| 决策点 | 推荐方案 | 关键 trade-off |
|---|---|---|
| 模块拆分 | 按业务线 + 组件化 | 短期成本 vs 长期效率 |
| 包大小 | App Bundle + 按需 module | 原生支持 vs 复杂度 |
| 页面通信 | Arouter(跳转)+ Hilt(服务) | 编译期校验 vs 学习成本 |
| UI 框架 | Compose(渐进式迁移) | 声明式 vs 迁移成本 |
| 构建优化 | KSP + R8 + Remote Cache | 速度 vs 维护 |
| 测试 | 四层金字塔 70/20/8/2 | 覆盖率 vs 维护成本 |
| 协作 | 模块 Owner + SemVer + CI | 质量 vs 效率 |
| 未来 | Compose Multiplatform + KMP | 跨平台 vs 平台深度 |