4. Gradle Build Scan 简介

说到构建速度优化,很多人一上来就去翻 build.gradle,改配置、调参数,忙活半天发现没啥效果。嗯,我刚开始也这么干过。后来踩了一次坑,花了两天时间排查一个第三方插件导致的构建变慢,最后发现其实用 Build Scan 看一眼就能定位。

Build Scan 是什么?说白了就是 Gradle 提供的构建过程快照。它能把你整个构建过程的每一步——任务耗时、依赖解析、缓存命中、配置阶段开销——统统记录下来,生成一个可交互的网页报告。我个人习惯把它叫做「构建者的 X 光机」。

核心思想:不要猜,直接看数据。Build Scan 帮你把构建过程可视化,瓶颈在哪一清二楚。

Build Scan 知识体系与排查流程 Gradle Build Scan 任务耗时分解 依赖解析展示 缓存命中分析 每个任务执行时间 任务依赖链瀑布图 依赖解析总耗时 冲突与版本耗时 缓存 miss 原因 变更输入导致未命中 生成 Build Scan → 定位瓶颈 → 针对性优化 提示:AGP 7.0+ 默认支持 Build Scan,只需添加 --scan 参数即可生成。 无需额外插件,一次构建就能拿到完整报告。

把 Build Scan 跑起来

使用 Build Scan 非常简单。AGP 从 4.2 开始就原生支持了,到了 AGP 7.0 以后基本开箱即用。

你只需要在构建命令后面加个 --scan 参数:

./gradlew assembleDebug --scan

构建完成后,控制台会输出一个链接。点开它,你就进入了 Build Scan 的世界。

我个人习惯在 CI 上也加上 --scan。每次构建完自动生成一个链接,粘贴到任务评论里。排查问题的时候简直不要太方便。

如果你用的是 AGP 7.0+,连 Gradle Enterprise 都不需要额外配置。第一次运行时会提示你同意条款,同意一次以后就不再问了。

Build Scan 能告诉你什么?

我挑几个最有用的页面说。

页面 / 标签 核心作用 我重点关注
Performance 总耗时、配置阶段耗时、任务耗时、GC 时间 配置阶段 > 5s 就要警惕
Timeline 瀑布图展示每个任务的起止时间、并行情况 看有没有长串串行任务阻塞
Dependencies 依赖解析过程、版本冲突、依赖树 检查是否解析了过多 SNAPSHOT
Cache 构建缓存命中/未命中详情 看哪些任务总是 miss,原因是什么
Test 测试执行时间分布 找慢的单测,考虑并行或拆模块

用瀑布图抓「慢任务」

Timeline 页面是网络瀑布图的核心。它会按时间轴展示所有任务的执行过程。

你想想看,如果一个 Task 耗时 30 秒,而其他 Task 早就跑完了,那这个 Task 就是瓶颈。点进去还能看到它的输入、输出、缓存 key 等信息。

我曾经遇到过一个问题:每次构建 release 版本都要多花 40 秒。打开 Timeline 发现 transformClassesAndResourcesWithProguardForRelease 任务独占 CPU 跑了一分钟,其他任务全在等它。后来通过给 ProGuard 分配更多堆内存解决了。

注意:Timeline 里每个任务的颜色代表任务类型(绿色=成功,红色=失败,灰色=up-to-date,蓝色=FROM-CACHE)。别把灰色的 up-to-date 任务当成耗时任务。

Dependencies 页面:揪出依赖耗时

依赖解析是很多人容易忽略的瓶颈。尤其是在使用了大量动态版本(2.+)或者 SNAPSHOT 版本时,Gradle 要每次都去远程仓库检查更新,拖慢构建。

Build Scan 的 Dependencies 页面会展示每个依赖的解析耗时、来源仓库、版本冲突。我个人常用的套路是:

  • 找到解析耗时最长的几个依赖
  • 检查它们是否都是 SNAPSHOT 或动态版本
  • 如果可能,改为固定版本号
  • 如果必须用 SNAPSHOT,考虑配置 changing=true 加较长的缓存过期时间

举个例子,有一次我团队的构建每次都要花 8 秒解析某个 SNAPSHOT 依赖。改了固定版本后,直接降到 0.2 秒。

Cache 页面:看你的缓存有没有生效

Build Scan 的 Cache 页面会把所有任务按缓存结果分类:

  • FROM-CACHE:从本地缓存取到了
  • REMOTE-CACHE:从远程缓存下载了
  • UP-TO-DATE:没变,直接跳过
  • EXECUTED:重新执行了

如果某个任务反复出现 EXECUTED,说明它的输入(如文件路径、依赖版本、甚至环境变量)一直在变。点进去看「Inputs」就知道原因了。

我记得有一次,某个模块的 compileDebugJavaWithJavac 每次都执行,查了 Inputs 发现是因为构建脚本里动态生成了一个版本号文件,导致每次输入都不同。后来把版本号文件排除在增量检查之外就解决了。

避坑指南

我曾经遇到过一个问题:Build Scan 生成后链接在 7 天后就失效了,需要登录 Gradle 才能查看。如果你需要长期保留报告,建议配置 Gradle Enterprise 或使用 --no-scan 后自行上传。

另外,Build Scan 默认只保留最新 5 次扫描记录。如果你是在 CI 上批量生成,记得通过 Gradle Enterprise 的 API 把关键构建的扫描结果保存下来。

一句话总结:Build Scan 是分析 AGP 构建性能的首选工具。拿到它,你就不再需要靠猜来优化构建速度了。