04.Gradle缓存
04.Gradle 缓存
Gradle 缓存
Gradle 内存缓存
Gradle 内存缓存主要是通过 Gradle Daemon 进程 (即守护进程) 实现的。
开启 Gradle Daemon(Gradle 3.0 之后守护进程默认开启)
在 gradle.properties 中设置 org.gradle.daemon=false
Gradle Daemon 优势
- 多次构建之间重用,只初始化一次
Gradle Daemon 是一个长期存在的进程,在多次构建之间,它将空闲地等待下一个构建;多个构建只需要初始化一次,而不是每个构建都需要初始化 - JVM 的 JIT 优化,构建次数越多构建越快
JVM 有运行时代码优化 (JIT),在代码执行过程中逐渐优化;根据 HotSpot 实现表明,JIT 优化通常需要 5~10 次构建才能稳定,因此 Daemon 的第一次构建和第十次构建之间的构建时间差异可能非常大 - 多次构建之间可以对构建脚本、构建插件、构建数据等进行内存缓存,以加快构建速度
- 可以检测两次构建之间的文件系统的变化,并计算出需要重新构建的文件,方便增量构建
Gradle 项目缓存
项目缓存主要存储在根目录的 .gradle
和各个 module 的 build
目录,其中 configuration-cache 存储在 .gradle
目录,而各个 task 的执行结果存储在 build 目录
configuration-cache
缓存配置阶段的结果
开启 configuration-cache:
1
2
3
# configuration cache
org.gradle.unsafe.configuration-cache=true
org.gradle.unsafe.configuration-cache-problems=warn
configuration-cache 适配:
不要在 task 执行阶段调用外部不可序列化的对象(如 Project 和 Variant)
1
2
3
4
5
6
7
8
android {
applicationVariants.all { variant ->
def mergeAssetTask = variant.getMergeAssetsProvider().get()
mergeAssetTask.doLast {
project.logger(variant.buildType.name)
}
}
}
如上所示,在 doLast 阶段调用了 project 与 variant 对象,这两个对象是在配置阶段生成的,但是又无法序列化,因此这段代码无法适配 Configuration Cache,需要修改如下:
1
2
3
4
5
6
7
8
9
android {
applicationVariants.all { variant ->
def buildTypeName = variant.buildType.name
def mergeAssetTask = variant.getMergeAssetsProvider().get()
mergeAssetTask.doLast {
logger(buildTypeName)
}
}
}
task cache
Gradle 本机缓存
Gradle 本机缓存即 GRADLE USER HOME 路径 (~/.gradle
) 下的 cache 目录
缓存更换目录:
新建 GRADLE_USER_HOME
环境变量,重启计算机即可
Build Cache
Build Cache 默认未开启
- 命令行添加
--build-cache
,Gradle 将只为此次构建使用 Build Cache - gradle.properties 添加
org.gradle.caching=true
,Gradle 将尝试为所有构建重用以前的构建的输出,除非通过--no-build-cache
禁用了
可缓存 Task
build cache 命中时,该 Task 会被标记为 FROM-CACHE
本地依赖缓存
所有远程下载的 aar 都在 cache/modules-2
目录下,这些 aar 可以在本地所有项目间共享,通过这种方式可以有效避免不同项目之间相同依赖的反复下载
我们应该尽量使用稳定依赖,避免使用 动态 (Dynamic) **或者快照 (SNAPSHOT) **版本依赖
使用稳定依赖版本,当下载成功后,后续再有引用该依赖的地方都可以从缓存读取, 避免缓慢的网络下载。而动态和快照这两种版本引用会迫使 Gradle 链接远程仓库检查是否有更新的依赖可用, 如果有则下载后缓存到本地.默认情况下,这种缓存有效期为 24小时
. 可以通过以下方式调整缓存有效期:
1
2
3
4
configurations.all {
resolutionStrategy.cacheDynamicVersionsFor(10, "minutes") // 动态版本缓存时效
resolutionStrategy.cacheChangingModulesFor(4, "hours") // 快照版本缓存时效
}
动态版本和快照版本会影响编译速度, 尤其在网络状况不佳的情况下以及该依赖仅仅出现在内部 repo 的情况下. 因为 Gradle 会串行查询所有 repo, 直到找到该依赖才会下载并缓存. 然而这两种依赖方式失效后就需要重新查询和下载。
同时这动态版本与快照版本也会导致 Configuration Cache 失效,因此应该尽量使用稳定版本
Gradle 远程缓存
镜像 repo
Gradle 下载 aar 有时非常耗时,一种常见的操作时添加镜像 repo,比如公开的阿里镜像等。或者部署公司内部的镜像 repo,以加快在公司网络的访问速度,也是很常见的操作。
关于 Gradle 仓库配置还有一些小技巧:Gradle 在查找远程依赖的时候, 会串行查询所有 repo 中的 maven 地址, 直到找到可用的 aar 后下载. 因此把最快和最高命中率的仓库放在前面, 会有效减少 configuration 阶段所需的时间.
除了顺序以外, 并不是所有的仓库都提供所有的依赖, 尤其是有些公司会将业务 aar 放在内部搭建的仓库上. 这种情况下如果盲目增加 repository 会让 Configuration 时间变得难以接受. 我们通常需要将内部仓库放在最前, 同时明确指定哪些依赖可以去这里下载:
1
2
3
4
5
6
7
8
9
repositories {
maven {
url = uri("http://repo.mycompany.com/maven2")
content {
includeGroup("com.test")
}
}
// ...
}
远程 Build Cache
Build Cache 不仅可以保存在本地 ($GRADLE_USER_HOME/caches), 也可以使用网络路径。在 settings.gradle 中加入如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// settings.gradle.kts
buildCache {
local<DirectoryBuildCache> {
directory = File(rootDir, "build-cache")
// 编译结果是否同步到本地缓存. local cache 默认 true
push = true
// 无用缓存清理时间
removeUnusedEntriesAfterDays = 30
}
remote<HttpBuildCache> {
url = uri("https://example.com:8123/cache/")
// 编译结果是否同步到远程缓存服务器. remote cache 默认 false
push = false
credentials {
username = "build-cache-user"
password = "some-complicated-password"
}
// 如果遇到 https 不授信问题, 可以关闭校验. 默认 false
isAllowUntrustedServer = true
}
}
通常我们在 CI 编译脚本中 push = true,而开发人员的机器上 push = false 避免缓存被污染。
要实现 Build Cache 在多个机器上的共享,需要一个缓存服务器,官方提供了两种方式搭建缓存服务器: Docker 镜像和 jar 包。
远程 Build Cache 应该也是一个可行的方案,试想如果我们有一个高性能的打包机,当每次打码提交时,都自动编译生成 Build Cache,那么开发人员都可以高效地复用同一份 Build Cache,以加快编译速度,而不是每次更新代码都需要在本机重新编译。
修改 GRADLE 缓存目录
定义 GRADLE_USER_HOME
环境变量,值为新的 Gradle 缓存目录