一、Android Build 系统概述
什么是 Build 系统
Build 系统,说白了就是一套自动化工具链。它把源代码、资源文件、第三方库这些东西,按照规则组合起来,最终生成一个可运行的 APK 或系统镜像。
我刚开始接触 Android 时,以为 Build 就是点一下「运行」按钮。后来才发现,背后藏着大量工作:编译、链接、打包、签名、优化……每一步都可能出问题。
Android 的 Build 系统基于 Soong(以前是 Make),用 Blueprint 文件(.bp)来描述模块。你写一个 Android.bp,告诉系统「我要编译这个库」「那个可执行文件依赖谁」,剩下的交给 Build 系统处理。
核心概念:Build 系统 = 编译规则 + 依赖管理 + 产物输出
为什么需要 Build 系统
你想想看,一个 Android 项目少则几百个文件,多则上万个。手动编译?不现实。
Build 系统解决了三个核心问题:
- 自动化——一条命令搞定全部编译,不用人工介入
- 增量编译——只改了一个 Java 文件,没必要重新编译所有 C++ 代码
- 依赖管理——A 模块依赖 B 模块,Build 系统会自动先编译 B
我在项目中遇到过最典型的场景:同事改了底层库的接口,上层模块没重新编译,运行时直接崩溃。有了 Build 系统的依赖追踪,这种问题基本不会发生。
个人经验:我习惯在修改代码后先执行 m(全量编译),确认没问题再用 mm(单模块编译)快速迭代。前者保稳,后者提速。
Android 构建流程全景图
整个构建流程,可以分成几个大阶段。我画了一张图帮你理解:
这张图展示了从源码到产物的完整链路。每个阶段都有专门的工具负责,我挑几个重点说说:
阶段详解
① 源码准备——不只是 .java 文件。AIDL 文件需要生成桩代码,.aidl 转 .java 这一步经常被新手忽略。资源文件要经过 aapt2 编译,生成 .flat 格式的中间产物。
② 编译阶段——Java/Kotlin 编译成 .class,C/C++ 编译成 .o。这里有个坑:不同模块的编译标准可能不一样。我曾经遇到过 NDK 代码用旧版编译器能过,换了新版就报错的情况。
避坑指南:我曾经在编译系统镜像时,发现某个 .so 库一直没更新。查了半天,原来是 Build 系统认为它「未变更」,跳过了重新链接。解决方案是执行 touch 强制触发重新编译。
③ 链接与打包——D8 把 .class 转成 DEX,R8 做代码压缩和混淆。这一步很关键,直接影响 APK 大小和运行性能。我习惯在发布前开启 R8 全量混淆,但调试阶段会关掉,不然堆栈信息根本看不懂。
④ 签名与对齐——APK 必须签名才能安装。debug 签名和 release 签名不同,千万别搞混。zipalign 对齐是为了让 APK 在运行时能更快地映射到内存,4 字节对齐是硬性要求。
构建系统的核心文件
Android 项目里,有几个文件是 Build 系统的「指挥中心」:
| 文件 | 作用 | 备注 |
|---|---|---|
| Android.bp | 定义模块的编译规则 | Soong 系统的核心配置文件 |
| Android.mk | 旧版 Make 规则文件 | 逐渐被 .bp 替代,但仍有项目在用 |
| BoardConfig.mk | 定义硬件平台相关配置 | 比如 CPU 架构、分区大小 |
| build/make/core/ | Build 系统核心逻辑 | 一般不需要修改,但值得了解 |
我个人习惯先看 Android.bp,里面定义了模块名、源文件、依赖库、编译标志等。举个例子:
// Android.bp 示例
cc_library_shared {
name: "libhello",
srcs: ["hello.cpp"],
shared_libs: ["liblog"],
cflags: ["-Wall", "-Werror"],
target: {
android: {
cflags: ["-DANDROID"],
},
},
}
这段代码告诉 Build 系统:编译一个名为 libhello 的共享库,源文件是 hello.cpp,依赖 liblog,开启所有警告并视为错误。Android 平台编译时额外定义 ANDROID 宏。
小技巧:如果你不确定某个模块的编译配置,可以用 showcommands 参数查看完整编译命令。比如 m showcommands,会打印出每一步执行的命令行。
构建系统的演进
Android Build 系统经历过几次大改。早期用 GNU Make,后来引入 Soong(基于 Go 语言),现在逐步迁移到 Bazel。每次演进都是为了解决同一个问题:编译速度。
我记得 Android 7.0 之前,全量编译一次要 40 分钟。Soong 引入后,增量编译快了很多。到了 Android 10,R8 替代 ProGuard,编译时间又缩短了一截。
嗯,这里要注意:虽然工具在变,但核心思想没变——自动化、增量、依赖管理。你掌握了这些底层逻辑,换什么工具都能快速上手。