构建阶段与生命周期:配置、依赖解析、编译、打包、测试

说到AGP版本升级,很多朋友第一反应就是改个com.android.tools.build:gradle版本号。但我跟你说,真正的坑往往藏在构建管道的每个拐角处。我从Gradle 3.0一直摸到现在的8.x,每次版本跳跃,那些所谓“内部优化”都会悄悄改变你项目里某个阶段的网络行为。

这一章,咱们就来拆解构建的五个核心阶段。搞清楚每个阶段在做什么,网络请求发生在哪里,你才能看懂后面的瀑布图。

核心观点:AGP版本升级本质上是重构了这五个阶段的任务依赖图和执行机制。过去老版本有些串行工作,现在变成了并行;过去某些阶段放在本地执行,现在可能触发远程解析或插件下载。说白了,瀑布图的变化就是这些重新配速的结果。

阶段一:配置(Configuration)

我个人喜欢把配置阶段比作建筑师画设计图。Gradle先读取你所有的.gradle文件,解析buildscript、allprojects、subprojects里的闭包。这时候,Gradle会创建Project和Task对象,但还不执行任何实际动作。

网络请求在这个阶段主要出现在两处:

  • 插件仓库解析:Gradle要从你声明的仓库(比如google()、mavenCentral())下载AGP插件本身。我遇到过一个大坑——公司内网代理设置不对,结果配置阶段卡在下载com.android.tools.build:gradle:8.2.0的pom文件上,一卡就是三分钟。
  • 依赖坐标声明:虽然只是声明,但如果你用了dependencies { implementation 'com.example:lib:1.+' }这种动态版本,配置阶段就会联网去查最新版本号。嗯,这里要注意:新版AGP的依赖解析引擎改用了更激进的缓存策略,动态版本查询频率提高了,网络瀑布图上会多出一小簇请求。

升级后的变化:从AGP 7.0开始,配置阶段默认启用了配置缓存(Configuration Cache)。如果项目没配合好,比如用了自定义buildscript闭包里动态生成task,配置缓存会失效,网络请求就会在每次构建时重复执行——瀑布图上出现一模一样的请求片段。

⚠️ 我曾经在迁移到AGP 8.0时,发现配置阶段多出了若干“get metadata”请求,排查半天是因为一个老旧的第三方插件在配置阶段就触发了远程校验。解决办法:升级该插件或改用dependencyResolutionManagement统一管理仓库。

阶段二:依赖解析(Dependency Resolution)

配置阶段只是“说”我要谁,依赖解析阶段才是真正“找”谁。Gradle会解析所有依赖的版本冲突、传递依赖,并下载jar/aar到本地缓存。你想想看,这个阶段是网络瀑布图的主战场。

核心流程三步走:

  1. 解析元数据:下载每个依赖的.pom或.module文件,读取依赖树。
  2. 冲突解决:AGP会按照force、strictly、require等规则帮你选版。老版本如果遇到冲突,干脆抛异常;新版本会尝试自动降级,但可能会多拉一个意想不到的版本——网络瀑布图上偶尔多出一段独立的分支。
  3. 下载构件:真正下载aar/jar。注意,这里有一个关键差异:AGP 4.0以前,依赖解析是纯串行的——一个一个库按顺序下载。从7.0开始,Gradle引入了并行依赖解析,多个库的元数据可以同时拉取。瀑布图上就会看到原本一个长条变成几个叠加的小条。
AGP版本依赖解析模式网络瀑布图特征
3.x ~ 4.x串行一个依赖接一个依赖,长条+长串
5.x ~ 6.x部分并行(元数据+构件分开)前半段密集短条,后半段稀疏长条
7.0+全并行 + 智能缓存极短时间密集爆发,然后迅速收敛

我在项目中遇到过的情况是:升级到AGP 8.0后,依赖解析阶段的总时长从原来的40秒降到12秒,但网络瀑布图上同时发起的连接数暴增到35个,把公司内网的Nginx直接打挂了。后来给CI加了并发限流才稳住。

💡 建议:如果升级后网络瀑布图在依赖解析阶段出现大量HTTP 4xx/5xx,先检查仓库镜像配置。很多企业内网用了第三方代理,并行请求下容易触发限流或超时。可以试试--max-workers=4降低并行度。

阶段三:编译(Compilation)

编译阶段是CPU和内存的战场,但网络请求依然会意外乱入。为什么会这样?我举两个常见的场景:

  • 注解处理器(APT / KAPT):比如你用Room、Dagger、Glide,这些处理器在编译时会生成代码。如果处理器本身依赖了某些工具库(比如com.squareup:javapoet),而且你在kapt配置里用了动态版本,就会触发二次依赖解析。
  • 增量编译的元数据同步:从AGP 4.2开始,增量编译会记录每个Java文件的上次编译状态。如果开启了android.enableR8.fullMode=true,有时候R8会从远程拉取一些“程序数据库”(多语言字符串等,看具体配置)。

升级后最容易踩的坑是KAPT → KSP的迁移。我用很多项目从AGP 7.x升级到8.x时,KSP的依赖下载机制不一样了——老版KAPT是把注解处理器当作编译类路径的一部分直接下,新版KSP需要单独声明ksp配置,网络瀑布图上会多出一条“ksp-classpath”的请求线。

阶段四:打包(Packaging)

打包阶段就是把所有DEX、资源、原库文件捏成一个APK。这里有两个网络参与的点:

  • R8/ProGuard的映射文件下载:如果你在混淆配置里用了-libraryjars指向远程仓库,或者依赖了某些只提供混淆后的SDK(比如微信支付、支付宝老版本),打包阶段会去下载这些.jar。
  • 应用签名服务:现在很多团队用Play App Signing或者第三方远程签名服务,打包阶段需要上传未签名的APK并下载签名后的APK。这条网络请求在瀑布图上非常显眼,因为它发生在打包阶段的最后。

说实话,我在AGP 7.3项目中遇到过打包阶段突然多了几百KB的下载量——后来发现是因为新版本把android:extractNativeLibs="false"这个属性改成了默认未压缩,但实际还是读取了远程的native库元数据来校验。这种隐性流量在旧版本根本没有。

阶段五:测试(Testing)

测试阶段,尤其是仪器化测试(Instrumented Test)的网络行为最容易忽略。当你跑./gradlew connectedCheck时,AGP会:

  • 下载或更新测试用的Android系统镜像(如果设备是模拟器,且你开了自动更新)。
  • 上传APK和测试APK到设备/模拟器。
  • 测试框架(如Espresso)本身有依赖:如果项目里用了androidx.test:runner,可能会触发子依赖的元数据重解析。

升级后要注意:从AGP 7.2开始,测试任务默认启用并行执行(如果你有多个设备或模拟器)。网络瀑布图上会看到多条“测试APK上传”的短线重叠在一起。我曾经因为测试设备列表配置了4个模拟器,结果Kubernetes集群内的网络带宽吃满,导致整个CI挂掉——后来限制maxParallelForks=2才解决。

一句话总结:每个阶段都可能包含隐性网络请求。AGP版本升级后,这些请求的时序、并发度、缓存策略都会变。瀑布图就像心电图——读懂它,你才能诊断构建的“心脏病”。
AGP构建阶段与网络请求关联图 配置 解析Gradle脚本 下载插件POM 依赖解析 解析元数据 下载AAR/Jar 编译 注解处理器 增量元数据 打包 R8映射文件 远程签名 测试 镜像下载 APK上传 网络请求流(隐性流量来源) 插件仓库POM 依赖元数据/构件 注解处理器依赖 R8映射/签名 测试镜像/APK 升级后瀑布图典型变化 旧版:串行单一长条 → 新版:并行密集爆发 缓存策略变化:配置缓存失效 → 重复请求增加 并发度提升:依赖解析可能打爆内网代理

实践:如何定位瀑布图中的问题阶段

升级完AGP,拿到一张新的网络瀑布图,你怎么快速断定是哪个阶段出了问题?我总结了一个三步排查法:

  1. 看时间线头部:如果所有网络请求集中在构建启动后前10秒,且大量重复请求,查配置阶段的插件和缓存。
  2. 看请求量峰值:如果在某个时间段同时爆发30+个请求,看依赖解析阶段的并行下载量。去~/.gradle/caches看一眼,如果缓存目录异常大,说明旧缓存没被清除。
  3. 看尾部异常:如果最后出现一个孤零零的大文件下载(比别的请求都长5倍),查打包或测试阶段的远程签名/镜像。

举个例子,我去年迁移一个20模块的项目到AGP 8.1,瀑布图在依赖解析阶段显示有38个请求连续并发,但实际我们只声明了12个外部依赖。后来发现是mavenCentral()和jcenter()两个仓库里各有一份同名依赖,Gradle串行走了两个仓库去验证——虽然新版支持并行,但冗余的仓库声明反而拖慢了速度。删掉jcenter()后请求数降到12个,时间缩短60%。

💡 小技巧:用./gradlew build --info跑一次,看日志里每个阶段的耗时分布。比对着瀑布图的时间轴,你就能知道每个时间段正在执行哪个阶段的任务。我习惯把这两个时间线叠在一起分析——比单看任一个都直观得多。