第2章 Gradle守护进程与构建缓存优化

Gradle 守护进程与构建缓存是 Android 构建加速的“双引擎”。守护进程避免 JVM 冷启动与类加载开销,构建缓存复用历史输出,增量编译只重编译变更文件。本章围绕原理、参数调优、缓存策略和增量编译实现展开,并提供可复现的配置命令与调试方法。

2.1 守护进程参数调优

守护进程是可复用的后台 Gradle 运行时。核心调优点是 JVM 堆内存、生存策略和并行工人数。

参数作用建议值
org.gradle.jvmargs守护进程 JVM 参数(堆、GC)-Xmx2048m -XX:MaxMetaspaceSize=512m
org.gradle.parallel并行执行任务true
org.gradle.daemon.keepalive闲置超时(分钟)30
org.gradle.caching启用构建缓存true
典型 gradle.properties 配置:
org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
org.gradle.parallel=true
org.gradle.daemon.keepalive=30
org.gradle.caching=true
堆内存建议:物理内存的 50%~70% 分配给守护进程,但不超过 4GB(AGP 常规场景)。HeapDumpOnOutOfMemoryError 可辅助定位 OOM 原因。

2.2 构建缓存策略配置

构建缓存包括本地缓存(~/.gradle/caches/build-cache-1)和远程缓存(HTTP 服务)。缓存键由任务输入(文件、属性、类路径等)的哈希组成。

类型存储位置适用场景
本地缓存用户目录单人开发、本地构建加速
远程缓存HTTP 服务器(如 Redis、S3)CI/CD、团队共享构建输出

在 settings.gradle.kts 或 settings.gradle 中配置:

// settings.gradle.kts
buildCache {
    local {
        isEnabled = true
        directory = File(rootDir, ".build-cache")
        removeUnusedEntriesAfterDays = 7
    }
    remote {
        isEnabled = true
        url = uri("https://build-cache.example.com/cache/")
        isPush = true
        credentials {
            username = "cacheUser"
            password = System.getenv("CACHE_PASSWORD")
        }
    }
}
缓存键冲突:若远程缓存地址不可达或认证失败,Gradle 会静默降级为本地缓存。确保 CI 环境能正常推送和读取缓存,否则可能造成缓存污染或命中率下降。

2.3 增量编译原理

增量编译指只编译自上次构建以来发生变更的源文件或模块。Java 使用 compileAvoidance(ABI 分析),Kotlin 使用增量编译标记。核心决策流程:

  1. Gradle 对每个编译任务计算输入快照(文件内容哈希、类路径哈希)。
  2. 若任务未命中构建缓存,则检查增量编译标记文件(如 .gradle/7.5/compileJava/incremental)。
  3. 根据文件时间戳或哈希差异,筛选出变更的源文件。
  4. 仅对变更文件及其直接依赖文件(受类型层次影响)执行编译。
  5. 输出新的类文件,更新增量标记。

若增量编译不可用(如注解处理器、KAPT、大量接口变更),Gradle 会回退到全量编译。

通过 --info 日志可查看增量编译决策:Task ':app:compileDebugKotlin' is not up-to-date. Reason: Input file ... has changed.

守护进程 · 构建缓存 · 增量编译 协同流程 守护进程管理 JVM 堆内存、并行性、KeepAlive 构建请求路由 任务输入快照 → 缓存键 构建缓存层 本地缓存(~/.gradle/caches/) 键: 输入哈希 → .class / .ap_ 远程缓存(HTTP / S3) 推送/拉取 构建输出 增量编译引擎 文件变更检测 → ABI 分析 → 最小编译集 Java: compileAvoidance | Kotlin: incremental 编译输出 更新缓存 缓存命中 守护进程为每次构建提供低延迟 JVM 环境;构建缓存复用任务输出;增量编译在未命中缓存时精确缩小编译范围。

2.4 调试与诊断

使用以下命令查看守护进程状态和构建缓存命中率:

# 查看所有运行中的守护进程
gradle --status

# 强制停止守护进程(调试参数变更是需要)
gradle --stop

# 开启详细日志观察缓存行为
gradle assembleDebug --info 2>&1 | grep -i "cache\|incremental"

# 查询本地缓存利用率
ls -lh ~/.gradle/caches/build-cache-1/*.bin | wc -l

在 build/scans/ 目录生成构建扫描报告(需启用了 --scan 或 com.gradle.enterprise 插件),可图形化查看缓存命中与增量编译决策。

缓存命中率公式:命中次数 / (命中次数 + 未命中次数)。通常 CI 环境应达到 > 70%,本地开发 > 50% 为健康。

2.5 常见错误与优化实践

  • 守护进程 OOM:调整 -Xmx,或使用 -XX:+UseContainerSupport (容器环境)。
  • 缓存未被命中:检查任务输入是否包含路径、时间戳或不稳定插件。使用 --info 查看 Cache key ... 的组成。
  • 增量编译失效:KAPT 或自定义注解处理器强制全量编译。可尝试开启 kotlin.incremental.useClasspathSnapshot=true(实验性)。
  • 守护进程启动缓慢:避免同时启动多个守护进程的堆内存超过物理内存。设置 org.gradle.jvmargs 中 -Xms 与 -Xmx 相同以减少动态扩展。
  • 远程缓存认证失败:在 CI 日志中搜索 HttpBuildCache: could not store entry,确认 CACHE_USER 和 CACHE_PASSWORD 环境变量是否正确注入。
通用优化清单:① 保证 org.gradle.parallel、org.gradle.caching、org.gradle.daemon 均为 true;② 远程缓存使用 isPush = true 仅对 CI agent 开启;③ ~/.gradle/gradle.properties 中统一守护进程参数以避免项目覆盖。