如何启用 Gradle 的 Daemon、Parallel 和 Configuration-on-Demand 优化?
解读
在国内 Android 面试中,Gradle 构建速度是“必考题”。
一面往往先问“你平时怎么加快编译”,答出“开并行、开守护进程”只能算及格;
二面会追问“原理、版本差异、CI 怎么配、为什么有时反而更慢”,如果答不上来就会被判定为“只会背博客”。
因此,回答时要给出“命令行-IDE-CI 全场景配置 + 版本差异 + 踩坑经验”,让面试官确信你在 200+ 模块的大型项目里真刀真枪用过。
知识点
- Gradle Daemon:长期驻留的 JVM 进程,复用 JIT 缓存与内存,避免每次启动新 JVM。
- Parallel:多模块并行执行 task,要求模块间无隐式时序依赖。
- Configuration-on-Demand(COD):只配置参与构建的模块,跳过无关模块的 configuration 阶段,显著减少 Configuration 时间。
- 生效条件:Gradle 6.5+ 默认自带 Daemon;Parallel 与 COD 需显式开启;Kotlin DSL 与 Gradle 7.4+ 在 Android Studio 下默认启用 Parallel。
- 国内踩坑:
- Windows Defender/360 会锁 jar,导致 Daemon 频繁被杀;
- 多 SDK 镜像源时,COD 可能触发不同模块拉取不同版本缓存,出现“灵异”冲突;
- CI 容器内存不足,开 Parallel 容易 OOM,需配合 org.gradle.workers.max 限制并发数。
答案
一、本地开发(macOS/Linux/Windows 通用)
-
全局生效:在 ~/.gradle/gradle.properties 中追加
org.gradle.daemon=true // 其实 6.5+ 可省略,但显式写出可安抚面试官
org.gradle.parallel=true
org.gradle.configureondemand=true
// 国内建议再补两条
org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m
org.gradle.caching=true // 构建缓存,与并行叠加收益更大 -
项目级兜底:把上述内容再抄一份到 project/gradle.properties,防止新同学没配全局。
-
IDE 内校验:
Android Studio Arctic Fox 及以上,Settings → Build → Compiler 里三项全部勾选;
命令行 ./gradlew :app:assembleDebug --profile,打开 build/reports/profile 查看 Configuration 阶段是否缩短到 1 s 以内。
二、CI 场景(以国内最常用的 Jenkins + 自建 Kubernetes 为例)
-
镜像里预置 gradle.properties:
FROM openjdk:11-jdk
COPY ci-gradle.properties /root/.gradle/gradle.properties
内容相比本地去掉 daemon,改为
org.gradle.daemon=false // 容器一次性,Daemon 反而浪费内存
org.gradle.parallel=true
org.gradle.configureondemand=true
org.gradle.workers.max=2 // 4 GB Pod 限并发,防止 OOM
org.gradle.caching=true -
流水线里再加增量缓存卷:
volumes:- name: gradle-cache
hostPath: /data/gradle-cache
- name: gradle-cache
三、验证脚本(可当场写给面试官)
./gradlew --status // 查看 Daemon 存活
./gradlew :lib:build --scan // 生成 Build Scan,Parallel 与 COD 收益一目了然
四、版本差异一句话总结
Gradle ≤5.6:COD 实验性功能,必须同时开 –-configure-on-demand 开关;
Gradle 7.0:Parallel 默认开启,但 COD 仍需显式;
Gradle 8.0:COD 转正,官方称“稳定”,可放心开。
拓展思考
-
为什么 CI 里把 Daemon 关掉反而更快?
容器是一次性进程,Daemon 冷启动与 Warm-up 时间无法摊销,且占用内存导致 Kubernetes 节点频繁 OOMKill,整体排队时间 > Daemon 带来的编译收益。 -
Parallel 开启后,测试任务顺序错乱导致 flaky test 怎么解?
在 test 任务里显式加 mustRunAfter 或 finalizedBy,或者把 flaky 测试单独拆成 module,禁用并行:
tasks.withType(Test).configureEach {
if (name.contains("flaky")) maxParallelForks = 1
} -
Configuration Cache(Gradle 7.5+ 稳定)与 COD 的区别?
COD 只减少 configuration 入口,Configuration Cache 把整个 task graph 序列化到磁盘,二次构建直接加载,二者可叠加,但 Android 项目需插件全部适配,目前 AGP 8.1 已官方支持,可在 gradle.properties 加
org.gradle.unsafe.configuration-cache=true
提前体验,面试中抛出此点可展示技术前瞻性。