第1章 AGP构建流程与增量编译原理
AGP(Android Gradle Plugin)的构建过程本质上是 Gradle Task 的有向无环图(DAG)调度。理解构建阶段划分、增量编译触发条件以及 Task 缓存命中机制,是后续优化编译速度、适配不同 AGP 版本的基石。
1.1 Gradle 构建三大阶段
Gradle 的每一次构建都严格遵循三个阶段,AGP 在此基础上注入 Android 专有 Task。
| 阶段 | 核心行为 | AGP 注入的任务示例 |
|---|---|---|
| 初始化(Initialization) | 解析 settings.gradle,确定参与构建的 Project 列表 | 无 |
| 配置(Configuration) | 执行每个 Project 的 build.gradle,生成 Task 依赖图 DAG | android.applicationVariants, compileSdk 配置 |
| 执行(Execution) | 按 DAG 顺序执行 Task,生成产物 | mergeDebugResources, compileDebugKotlin |
关键结论:配置阶段越短,增量编译越敏感。每次配置阶段都会重新计算所有 Task 的输入/输出快照,即使后续执行阶段可能跳过。
1.2 增量编译触发条件
AGP 的增量编译并非所有 Task 默认启用,它只对声明了 @Incremental 注解的 Task 生效。触发条件分为三类:
- 源文件变更:Java/Kotlin 源文件、资源文件、AndroidManifest.xml 等被修改、新增或删除。
- 构建配置变更:build.gradle 中依赖版本、compileSdk、minSdk 等参数修改。
- Task 输出被清除:手动运行
clean、缓存目录被删除、CI 构建无本地缓存。
// 触发全量重新编译的典型配置变更
android {
compileSdk 34 // compileSdk 变化 → 所有处理资源/代码的 Task 重新执行
buildToolsVersion "35.0.0" // 仅影响部分打包 Task
defaultConfig {
minSdk 24 // 仅影响 manifest merger 及 multidex
}
}
常见误解:修改 build.gradle 中
compileOptions 的 Java 版本,大部分 Android Task 不会增量,而是直接跳过或全量重跑—因为 Task 输入参数(Java 版本)属于 Task 输入的一部分,变更后缓存失效。
1.3 Task 缓存命中机制
AGP 基于 Gradle 的 Build Cache(构建缓存)实现 Task 复用。缓存的键由Task 输入属性的快照和Task 类的 hash共同决定。
缓存命中条件:当前 Task 的所有输入(源文件、注解处理器选项、编译参数等)的 hash 与缓存的 hash 完全一致,且 Task 实现类未升级。
以下场景会触发缓存失效:
- 输入文件内容修改,但文件时间戳未变(跨机器构建常见)→ 依赖快照校验,使用
org.gradle.caching=true可缓解。 - Task 的
outputs.upToDateWhen自定义规则返回false。 - 本地缓存目录被清除,远程缓存不可达。
// gradle.properties 中强制启用远程缓存
org.gradle.caching=true
org.gradle.caching.local.enabled=true
org.gradle.caching.http.url=https://cache.example.com:5000
1.4 调试构建阶段与缓存
使用 Gradle 自带的 --info 或 --debug 日志可观察每个 Task 是否 UP-TO-DATE、FROM-CACHE 或 重新执行。
./gradlew assembleDebug --info | grep "Task :" | head -20
# 输出示例:
# Task :app:preBuild UP-TO-DATE
# Task :app:mergeDebugResources FROM-CACHE
# Task :app:compileDebugKotlin EXECUTING
调试命令:使用
./gradlew :app:tasks --group build 列出所有 Task 及其缓存属性;使用 --scan 生成构建扫描页面,直观查看阶段耗时。
1.5 性能与稳定性优化建议
- 将
org.gradle.parallel=true和org.gradle.caching=true加入gradle.properties。 - 避免在配置阶段执行 I/O 操作(如读取文件判断 SDK 版本),使用
afterEvaluate或task.doFirst延迟执行。 - 使用 AGP 的
android.enableR8.fullMode=true减少混淆后的 Task 输入哈希波动。 - 升级 Gradle 8.x + AGP 8.x,其中增量编译和缓存命中率显著提升(尤其是 Kotlin 编译)。
1.6 常见错误与应对
- “Task :app:compileDebugKotlin” 持续全量编译:检查 build.gradle 中是否在非配置阶段调用了
File.exists()或new File()构造路径,导致输入快照不稳定。 - 远程缓存命中但下载失败:配置
org.gradle.caching.http.connectTimeout=5000和readTimeout=30000,并确保远程缓存服务器返回正确的 ETag。 - 增量编译后产物签名不一致:禁用
android.injected.build.config.only,在release构建中使用isDefault = true的 signingConfig。