12、Android构建优化:构建性能分析(--profile)、配置缓存、并行构建、按需配置、优化依赖解析

说实话,做Android开发这么多年,我最怕听到的一句话就是:「你那个项目编译要多久?」

我记得刚带团队那会儿,有个模块编译一次要4分多钟。大家每天光等编译就能刷好几条短视频。后来我实在忍不了,花了两周时间专门搞构建优化,硬是把时间压到了40秒以内。今天我就把那些压箱底的经验,一个一个说给你听。

12.1 先诊断,再开药:用 --profile 分析构建性能

优化之前,你得先知道时间花在哪了。这就像看病,总得先拍个片子吧?

Gradle 自带的 --profile 参数就是你的「CT机」。用法很简单:

./gradlew assembleDebug --profile

跑完之后,在 build/reports/profile/ 目录下会生成一个 HTML 报告。里面会告诉你:

  • 每个 Task 花了多少时间
  • 配置阶段和任务执行阶段分别用了多久
  • 哪些 Task 是「慢吞吞大王」

我个人习惯:每次大版本升级 Gradle 或 AGP 之后,都会跑一次 --profile,看看有没有「退化」。有一次升级后我发现某个 Task 从 200ms 变成了 2s,查了半天发现是某个插件版本不兼容。嗯,这种坑踩过一次就长记性了。

12.2 配置缓存:让增量编译飞起来

配置缓存是 Gradle 6.6 之后引入的特性。说白了,就是把你项目的构建配置「快照」下来,下次构建时直接复用,不用重新跑一遍配置阶段。

开启方式很简单,在 gradle.properties 里加上:

org.gradle.configuration-cache=true

第一次开启时,Gradle 会跑一遍完整的配置流程,然后生成缓存。第二次开始,你就会发现配置阶段从十几秒变成了几百毫秒。

注意:配置缓存和某些插件不兼容。我曾经遇到过某个自定义插件在开启配置缓存后直接报错,排查了半天才发现是插件里用了 project.afterEvaluate 做了一些「脏操作」。解决方案是把那些逻辑移到 Task 内部去执行。

12.3 并行构建:别让 CPU 闲着

现在的开发机哪个不是 8 核 16 线程?但默认情况下 Gradle 只用一个线程跑任务,这不是暴殄天物吗?

开启并行构建也很简单:

org.gradle.parallel=true

这个参数会让 Gradle 把没有依赖关系的 Task 并行执行。比如你项目里有多个模块,它们之间没有依赖关系,那就可以同时编译。

我建议:并行构建的线程数可以设置为 CPU核心数 - 1。留一个核心给系统和其他应用,不然电脑会卡成幻灯片。我一般这么配:

org.gradle.workers.max=4

12.4 按需配置:只编译你需要的模块

你有没有遇到过这种情况:明明只改了一个模块的代码,结果 Gradle 把整个项目都配置了一遍?

按需配置(On-demand Configuration)就是解决这个问题的。开启后,Gradle 只会配置那些真正需要执行的模块。

org.gradle.configureondemand=true

举个例子,你只运行 ./gradlew :app:assembleDebug,那 Gradle 就不会去配置 :library 模块,除非 :app 依赖了它。

我曾经在一个 30+ 模块的项目里开启了这个选项,配置阶段从 25 秒降到了 3 秒。效果立竿见影。但要注意,如果你的项目里有很多动态依赖或者自定义的依赖解析逻辑,按需配置可能会出问题。遇到这种情况,建议先排查依赖配置是否合理。

12.5 优化依赖解析:别让网络成为瓶颈

依赖解析是构建过程中最容易忽略的「隐形杀手」。你想想看,每次构建都要去远程仓库拉依赖,网络稍微波动一下,整个构建就得等。

优化依赖解析,我总结了几个实用技巧:

  • 使用本地仓库镜像:把 Maven Central、Google 等仓库的依赖缓存到本地 Nexus 或阿里云镜像上。国内用阿里云镜像效果特别明显。
  • 锁定依赖版本:不要用 + 号版本号,比如 com.squareup.okhttp3:okhttp:4.+。这会让 Gradle 每次构建都去检查最新版本,浪费大量时间。
  • 减少动态依赖changing=true 的依赖要慎用,除非你确实需要每次都拉取最新快照。
  • 使用 Gradle 的依赖缓存:默认情况下 Gradle 会缓存依赖,但你可以通过 --offline 参数强制使用缓存,避免网络请求。
// 在 build.gradle 中锁定版本
configurations.all {
    resolutionStrategy {
        // 强制使用指定版本
        force 'com.squareup.okhttp3:okhttp:4.11.0'
        // 缓存动态依赖 24 小时
        cacheDynamicVersionsFor 24, 'hours'
        // 缓存 changing 模块 24 小时
        cacheChangingModulesFor 24, 'hours'
    }
}

一个小技巧:我习惯在 gradle.properties 里加上 org.gradle.caching=true,开启构建缓存。这样即使你切换分支或者清理了项目,只要依赖没变,就能从缓存里直接拿结果,不用重新下载。

12.6 知识体系总览

说了这么多,我把整个构建优化的核心逻辑画成了一张图,方便你理解:

Android 构建优化核心体系 构建优化 诊断分析 --profile 报告 配置缓存 configuration-cache 并行构建 parallel=true 按需配置 configureondemand 依赖解析优化 镜像仓库 · 版本锁定 · 缓存策略 目标:减少构建时间,提升开发效率

12.7 总结一下

构建优化这件事,说白了就是「找瓶颈、拆瓶颈」。先用 --profile 找到慢在哪,然后对症下药:配置缓存解决配置阶段慢的问题,并行构建让多核 CPU 物尽其用,按需配置避免不必要的模块加载,依赖解析优化则把网络延迟降到最低。

我个人的经验是,这五个优化点一起上,效果最明显。单独开一个两个,效果有限。你想想看,如果配置阶段快了,但依赖解析还在那慢慢拉,整体时间还是下不来。

嗯,今天就先聊到这。下一章我们继续深入,看看怎么用 Gradle 的 Build Scan 做更精细的性能分析。

避坑指南:我曾经在一个项目里同时开启了配置缓存和按需配置,结果发现某些 Task 的输入输出不一致,导致构建结果不可靠。后来我查了 Gradle 的官方文档,才知道这两个特性在某些场景下会有冲突。建议你先单独开启配置缓存,稳定运行一段时间后再开启按需配置。


公众号:蓝海资料掘金营,微信deep3321