一文了解Gradle 依赖管理(五)- 依赖管理缓存依赖
文章目录
- 1. 版本目录 (Version Catalogs)
- 1. 版本目录的概念与优势
- 2. 主要优势
- 3. 基本配置
- 4. 使用版本目录
- 5.使用外部版本目录文件
- 6.实际项目中的版本目录最佳实践
- 2. 依赖锁定(Dependency Locking)
- 1. 依赖锁定的概念与重要性
- 2. 主要优势
- 3. 如何启用依赖锁定
- 4. 生成锁定文件
- 5. 更新锁定依赖
- 6. 依赖锁定与版本目录结合使用
- 3. 依赖缓存与离线构建
- 1. Gradle 的依赖缓存机制
- 2. 配置缓存位置
- 3. 启用构建缓存
- 4. 离线模式构建
- 5. 依赖缓存的最佳实践
1. 版本目录 (Version Catalogs)
版本目录是 Gradle 7.0 引入的一项功能,它提供了一种集中管理依赖版本和依赖组的强大方式,尤其适合多模块项目。
1. 版本目录的概念与优势
版本目录是一种将依赖定义从构建脚本中抽离出来的机制,使依赖管理更加结构化、集中化,并支持更好的 IDE 支持。
2. 主要优势
1. 集中管理:在一个地方定义所有依赖版本和依赖组
2. 类型安全:提供类型安全的依赖访问,减少拼写错误
3. IDE 支持:更好的自动完成和导航支持
4. 依赖分组:按逻辑组织依赖
5. 版本控制:轻松更新和跟踪依赖版本变化
3. 基本配置
版本目录通常在 settings.gradle
或 settings.gradle.kts
文件中定义:
// 在settings.gradle中
dependencyResolutionManagement {
versionCatalogs {
libs {
// 定义版本
version('kotlin', '1.6.10')
version('coroutines', '1.6.0')
// 定义库
library('kotlin-stdlib', 'org.jetbrains.kotlin', 'kotlin-stdlib').versionRef('kotlin')
library('kotlin-reflect', 'org.jetbrains.kotlin', 'kotlin-reflect').versionRef('kotlin')
library('coroutines-core', 'org.jetbrains.kotlinx', 'kotlinx-coroutines-core').versionRef('coroutines')
// 定义依赖组(bundle)
bundle('kotlin-bundle', ['kotlin-stdlib', 'kotlin-reflect'])
// 定义插件
plugin('kotlin-android', 'org.jetbrains.kotlin.android').versionRef('kotlin')
}
}
}
在 Kotlin DSL 中(settings.gradle.kts
):
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
version("kotlin", "1.6.10")
version("coroutines", "1.6.0")
library("kotlin-stdlib", "org.jetbrains.kotlin", "kotlin-stdlib").versionRef("kotlin")
library("kotlin-reflect", "org.jetbrains.kotlin", "kotlin-reflect").versionRef("kotlin")
library("coroutines-core", "org.jetbrains.kotlinx", "kotlinx-coroutines-core").versionRef("coroutines")
bundle("kotlin-bundle", listOf("kotlin-stdlib", "kotlin-reflect"))
plugin("kotlin-android", "org.jetbrains.kotlin.android").versionRef("kotlin")
}
}
}
4. 使用版本目录
一旦定义了版本目录,我们就可以在构建脚本中使用它们:
plugins {
id(libs.plugins.kotlin.android.get().pluginId)
}
dependencies {
implementation(libs.kotlin.stdlib)
implementation(libs.coroutines.core)
implementation(libs.bundles.kotlin.bundle)
}
5.使用外部版本目录文件
对于更复杂的项目,您可以将版本目录定义移动到专用的 TOML 文件中, 当然这也是Android官方推荐的做法,在新的AS项目中,默认就是使用的 TOML文件:
# libs.versions.toml
[versions]
kotlin = "1.6.10"
coroutines = "1.6.0"
retrofit = "2.9.0"
okhttp = "4.9.3"
room = "2.4.1"
[libraries]
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }
retrofit-core = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
retrofit-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "retrofit" }
okhttp-core = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
okhttp-logging = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" }
room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
[bundles]
kotlin = ["kotlin-stdlib", "kotlin-reflect"]
coroutines = ["coroutines-core", "coroutines-android"]
retrofit = ["retrofit-core", "retrofit-gson", "okhttp-core", "okhttp-logging"]
room = ["room-runtime"]
[plugins]
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" }
然后在 settings.gradle
中引用这个文件:
dependencyResolutionManagement {
versionCatalogs {
libs {
from(files("libs.versions.toml"))
}
}
}
6.实际项目中的版本目录最佳实践
1. 按功能或技术栈分组:将依赖按照功能或技术栈分组,例如
network
、ui
、database
等。
2. 使用有意义的别名:为依赖创建直观的别名,如retrofit
而不是squareup-retrofit
。
3. 充分利用版本引用:避免重复的版本号声明,使用版本引用确保相关库使用相同版本。
4. 创建有用的 bundle:将常一起使用的依赖组合成 bundle,简化依赖声明。
5. 保持 TOML 文件的良好组织:使用注释和分组保持文件可读性。
例如以下:
# 网络相关依赖
[libraries.retrofit]
module = "com.squareup.retrofit2:retrofit"
version = "2.9.0"
# 注释说明特殊用途的依赖
[libraries.leak-canary]
module = "com.squareup.leakcanary:leakcanary-android"
version = "2.8.1"
# 仅在 debug 构建中使用
2. 依赖锁定(Dependency Locking)
依赖锁定是确保构建可重复性的重要技术,特别是当项目使用动态版本或版本范围时。
1. 依赖锁定的概念与重要性
依赖锁定将依赖图"冻结"在特定的时间点,记录解析的确切版本,确保后续构建使用相同的依赖版本,即使新版本发布。
2. 主要优势
- 构建可重现性:确保相同的源代码总是使用相同的依赖版本构建
- 防止意外升级:避免自动获取新发布的库版本
- 行为一致性:减少"在我的机器上能运行"问题
- 安全性:在审核新版本之前防止引入新依赖
3. 如何启用依赖锁定
在 settings.gradle
或特定项目的 build.gradle
中启用依赖锁定:
// 为所有配置启用依赖锁定
dependencyLocking {
lockAllConfigurations()
}
// 或者为特定配置启用
configurations.compileClasspath {
resolutionStrategy.activateDependencyLocking()
}
4. 生成锁定文件
一旦启用了依赖锁定,您需要生成锁定文件:
# 为所有配置生成锁定文件
./gradlew dependencies --write-locks
# 为特定项目生成锁定文件
./gradlew :app:dependencies --write-locks
这将在 gradle/dependency-locks
目录下创建锁定文件,每个配置一个文件。
5. 更新锁定依赖
当需要有意更新依赖版本时:
# 更新所有依赖锁
./gradlew dependencies --update-locks "com.squareup.retrofit2:*"
# 或者完全刷新所有锁
./gradlew dependencies --write-locks
6. 依赖锁定与版本目录结合使用
依赖锁定和版本目录可以很好地结合使用,这种组合确保您既有集中的版本管理,又有确切的依赖锁定,提供最大的可重复性和控制。
- 版本目录:声明您期望的依赖版本(理想状态)
- 依赖锁定:记录实际解析的版本(实际状态)
// 在build.gradle中
dependencies {
implementation libs.retrofit.core // 从版本目录
}
dependencyLocking {
lockAllConfigurations()
}
这里可能稍微难理解,我用个例子简单来描述一下。
假设我们开发的App中, Retrofit 2.9.0 依赖 OkHttp 4.9.0,但我们在版本目录中指定了更新的 OkHttp 4.9.3,
当没有依赖锁定时:
- 第一个开发者构建项目时会使用 OkHttp 4.9.3
- 下一个开发者几周后构建项目,可能 OkHttp 已更新到 4.9.5,他将使用 4.9.5
开启依赖锁定后:
- 所有开发者都会使用锁定文件中的精确版本 OkHttp 4.9.3
- 即使 OkHttp 发布了 4.9.5 版本,除非我们显式更新锁定文件,否则团队仍会使用 4.9.3
现在假设我们的目录版本中更新了Gson:
[versions]
# 更新了版本
gson = "2.9.0" # 从 2.8.5 更新
有了依赖锁定:
- 即使版本目录更新了,项目仍会使用锁定文件中的 2.8.5
- 团队可以讨论并决定何时更新锁定文件
- 当团队准备好时,可以显式更新锁定:
./gradlew dependencies --update-locks "com.google.gson*:*"
3. 依赖缓存与离线构建
有效管理依赖缓存可以显著提高构建性能,特别是在团队环境或 CI/CD 管道中。
1. Gradle 的依赖缓存机制
Gradle 在本地缓存所有下载的依赖,默认位置是用户主目录下的 .gradle/caches
目录。
2. 配置缓存位置
我们可以自定义缓存位置, 在 gradle.properties中:
# gradle.properties
org.gradle.caching=true
org.gradle.caching.debug=false
org.gradle.cache.dir=/custom/cache/directory
3. 启用构建缓存
构建缓存可以在配置中启用:
# settings.gradle
buildCache {
local {
enabled = true
}
}
4. 离线模式构建
离线模式强制 Gradle 只使用缓存的依赖:
# 命令行参数
./gradlew build --offline
# 或在 gradle.properties 中永久设置
org.gradle.offline=true
5. 依赖缓存的最佳实践
1. 共享缓存:考虑在团队之间或 CI 系统中共享缓存目录,减少重复下载
2. 定期清理:定期清理缓存,删除不再使用的依赖版本
./gradlew cleanBuildCache
3. 预热缓存:在 CI 构建前或开发环境设置中预热缓存
./gradlew downloadDependencies
4. 缓存备份:备份关键依赖的缓存,防止远程仓库不可用
5. 使用镜像或私有仓库:设置本地镜像或私有依赖仓库,减少外部依赖.