构建速度问题概述

说实话,AGP 升级这件事,我一开始是挺兴奋的。新特性、新优化,想着肯定能让构建更快。结果呢?项目一跑,直接傻眼——构建时间反而暴涨了 30% 以上。当时组里的同学都看我,我脸上挂不住啊。

后来我花了整整两周,翻了 Gradle 源码、看了 AGP 的 release notes,才慢慢搞清楚问题出在哪。今天这部分,我就把踩过的坑和总结的经验,一五一十讲给你听。

AGP 升级带来的"隐形"负担

很多人以为 AGP 升级就是换几个版本号,点个 Sync 就完事了。其实不然。我自己的项目从 AGP 4.2 升到 7.4,编译时间从 2 分半变成了 4 分钟。为什么?我列几个常见的"隐形杀手":

  • 增量构建失效:新版本改了任务依赖关系,以前能走增量的任务现在得全量跑。我遇到过资源压缩任务每次都要重跑所有图片——白白浪费 30 秒。
  • 新的编译优化带来的副作用:比如 R8 的优化更激进,但处理时间也更长。你想想看,原来 ProGuard 只处理 30 秒,现在 R8 要 1 分半。
  • 资源打包逻辑变化:AAPT2 升级后,资源 ID 分配不能完全复用,每次构建都得重新解析一遍资源表。这对大项目尤其致命。
  • 插件兼容性问题:一些第三方插件还没适配新 AGP,会强制走降级路径,甚至触发重复执行。

⚠️ 避坑指南:我曾经在升到 AGP 7.0 后,发现每次改一行代码都要等 2 分钟。最后定位到是 databinding 插件在新版里触发了全量资源处理。解决办法是升级到 7.1+ 并开启 android.databinding.enableV2。

怎样才算"慢"?——量化指标

没有数据支撑的优化都是耍流氓。我习惯先给团队定个基线:clean build 不能超过 5 分钟,incremental build 不能超过 30 秒。超过这个数,就得查问题了。

下面是我整理的一个参考表格(基于 500+ module 的中等项目):

阶段 AGP 4.2 平均耗时 AGP 7.4 平均耗时 升幅
增量编译(改 1 个 Java 文件) 12s 22s +83%
全量编译(clean build) 2m30s 4m10s +67%
Dex 处理(R8) 35s 58s +66%
资源打包 18s 32s +78%

看到这数据,你肯定想问:那到底怎么定位瓶颈?用 Gradle Profile?我试过,太粗。它只告诉你哪个 task 慢,但是为什么慢、跟其他 task 有什么依赖关系,完全看不出来。这时候,我就想到了网络瀑布图。

网络瀑布图——一种新的分析视角

说白了,网络瀑布图原本是前端用来分析页面加载性能的——每个请求什么时候开始、什么时候结束、并行度怎么样,一目了然。但你想啊,构建过程也是一堆 task 在按依赖关系"请求"资源,本质上跟页面加载没什么区别。

我花了点时间,把 Gradle 的 task 执行日志转成瀑布图格式,直接在浏览器里看。效果惊艳——一眼就能看出哪些 task 在排队等锁,哪些 task 可以并行但没并行。

举几个实际用瀑布图发现的问题:

  • 两个完全独立的任务(比如 mergeRes 和 compileJava)被同一个线程池限制,导致串行执行。
  • 某个插件在 preBuild 阶段加了 3 秒的同步锁,后续所有任务都等着。
  • 依赖里有个小版本冲突,导致每次构建都要重新解析整个依赖树。

💡 个人习惯:我一般先看瀑布图里有没有"断层"——就是一段很长的时间线里只有一个 task 在跑,其他都是空白。那基本上就是并行度没打满,或者有隐式依赖。

我常用的分析流程

这套流程我在三个项目里验证过,都成功把构建时间降回了升级前的水平。你可以直接拿来用:

  1. 对比基线:先跑一次 clean build + 一次 incremental build,记录总时间和各阶段耗时。
  2. 导出构建扫描:用 --scan 参数,或者直接采集 Gradle 输出的 JSON 日志。
  3. 生成瀑布图:我写了个小脚本(Python),解析 JSON 日志里的 task start/stop 时间,丢进一个 HTML 模板里渲染。
  4. 找"胖"任务和"闲"时间:哪个 task 占的时间最长?哪段时间所有 task 都在等?一一标记。
  5. 交叉验证:用 Android Studio 的 Profile 工具跟瀑布图对照,确保不是采集误差。
  6. 逐个击破:针对找到的瓶颈,调整并行度、优化依赖、升级插件版本。

记住:不要凭感觉优化。我见过有人一上来就调 org.gradle.parallel 和 max-workers,但问题其实是某个任务占用了大量 I/O 导致磁盘瓶颈。瀑布图可以帮你区分到底是 CPU 瓶颈还是 IO 瓶颈。

好了,理论说完,下面这张图是我自己画的分析框架,你看一眼就能明白整体思路:

构建速度瓶颈分析流程图 AGP 升级 构建变慢现象 传统 Gradle Profile 资源/编译瓶颈 网络瀑布图分析 定位瓶颈并优化 右侧虚线箭头表示:资源/编译瓶颈也可以通过瀑布图进一步分析,并非完全独立

嗯,这张图把整个排查思路串起来了。核心就是:不要只盯着 profile 看数字,要用可视化时间线把依赖关系暴露出来。