上一篇聊了 CI/CD 实践,这篇继续工程效率方向——依赖管理。多模块项目里,依赖版本不一致是个经典问题:模块 A 用 Retrofit 2.9,模块 B 用 2.11,编译不报错但运行时行为不一致。Version Catalogs 就是解决这个问题的。
传统依赖管理的问题
在 Version Catalogs 出现前,Android 项目管理依赖通常有三种方式:
1. 直接在 build.gradle 里写
dependencies {
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.okhttp3:okhttp:4.9.3")
}
问题:每个模块各自声明,版本散落各处,升级时要逐个改。
2. 用 ext 或 buildSrc 管理版本
// buildSrc/src/main/java/Dependencies.kt
object Versions {
const val retrofit = "2.11.0"
const val okhttp = "4.12.0"
}
object Deps {
const val retrofit = "com.squareup.retrofit2:retrofit:${Versions.retrofit}"
const val okhttp = "com.squareup.okhttp3:okhttp:${Versions.okhttp}"
}
比直接写好,但 buildSrc 每次改动都会触发全量重编译,大型项目改一个版本号要等两分钟。
3. Extra Properties
// 根 build.gradle
extra["retrofitVersion"] = "2.11.0"
// 子模块
dependencies {
implementation("com.squareup.retrofit2:retrofit:${rootProject.extra["retrofitVersion"]}")
}
能用但类型不安全,IDE 不提示,拼错了编译时才报。
Version Catalogs
Gradle 7.0 引入,7.4 成为稳定特性。通过一个 TOML 文件统一管理所有依赖:
# gradle/libs.versions.toml
[versions]
retrofit = "2.11.0"
okhttp = "4.12.0"
compose-bom = "2024.09.00"
kotlin = "1.9.24"
[libraries]
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
okhttp-logging = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" }
compose-bom = { module = "androidx.compose:compose-bom", version.ref = "compose-bom" }
[bundles]
network = ["retrofit", "okhttp", "okhttp-logging"]
[plugins]
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
在模块中使用:
dependencies {
// 直接引用
implementation(libs.retrofit)
implementation(libs.okhttp)
// 使用 bundle
implementation(libs.network)
// 使用 BOM
implementation(platform(libs.compose.bom))
}
Version Catalogs 的优势
类型安全:Gradle 生成
libs访问器,IDE 自动补全,拼错直接报红单文件管理:所有版本集中在 TOML 文件中
增量编译:改 TOML 不会触发 buildSrc 重编译
Bundle 支持:把相关依赖打包成一组,一行引用
Bundle 的妙用
把经常一起使用的依赖组成 Bundle:
[bundles]
androidx-lifecycle = ["lifecycle-runtime", "lifecycle-viewmodel", "lifecycle-livedata"]
compose-ui = ["compose-ui", "compose-ui-graphics", "compose-ui-tooling", "compose-material3"]
dependencies {
implementation(libs.androidx.lifecycle)
implementation(libs.compose.ui)
}
不用一个个 implementation,简洁很多。
BOM 的使用
BOM(Bill of Materials)是另一种版本管理方式,适合库的版本联动:
[versions]
compose-bom = "2024.09.00"
[libraries]
compose-bom = { module = "androidx.compose:compose-bom", version.ref = "compose-bom" }
compose-ui = { module = "androidx.compose.ui:ui", version = "none" }
compose-material3 = { module = "androidx.compose.material3:material3", version = "none" }
dependencies {
implementation(platform(libs.compose.bom))
implementation(libs.compose.ui) // 版本由 BOM 统一管理
implementation(libs.compose.material3)
}
BOM 适合管理一个生态的依赖(如 Compose、Firebase),Version Catalogs 适合管理全局依赖。两者可以结合使用。
依赖冲突排查
# 查看依赖树
./gradlew :app:dependencies --configuration releaseRuntimeClasspath
# 查看某个依赖的来源
./gradlew :app:dependencyInsight --dependency retrofit --configuration releaseRuntimeClasspath
常见冲突场景:两个库各自依赖了不同版本的同一个传递依赖。Gradle 默认选最高版本,但 API 不兼容时会崩。
解决方式:
// 强制使用某个版本
configurations.all {
resolutionStrategy {
force("com.squareup.okhttp3:okhttp:4.12.0")
}
}
// 排除某个传递依赖
implementation("com.example:lib:1.0") {
exclude(group = "com.squareup.okhttp3")
}
依赖自动化更新
手动检查更新太低效。几个工具:
Renovate
GitHub/GitLab CI 集成,自动发 PR 升级依赖:
// renovate.json
{
"extends": ["config:base"],
"schedule": ["before 6am on Monday"],
"packageRules": [
{
"groupName": "Kotlin",
"matchPackagePatterns": ["^org.jetbrains.kotlin"]
}
]
}
Gradle Versions Plugin
// build.gradle
plugins {
id("com.github.ben-manes.versions") version "0.51.0"
}
./gradlew dependencyUpdates # 检查最新版本
依赖治理建议
统一入口:所有第三方依赖走 Version Catalogs,不要在模块 build.gradle 里直接写版本号
定期升级:每月或每两周跑一次依赖检查,分批升级
大版本升级单独 PR:Retrofit 2.x → 3.x 这种大版本变化可能有 breaking change,单独 PR 测试
移除不用依赖:用
./gradlew dependencyAnalysis或 Knit 工具检查无用依赖安全扫描:在 CI 中集成 Dependabot 或 OWASP Dependency Check
小结
依赖管理看似简单但直接影响项目稳定性和构建效率。Version Catalogs 是当前最佳实践,新项目应该直接用,存量项目值得花一天迁移。配合 Renovate 做自动更新,基本可以告别手动管版本的日子。
下一篇聊聊 Android 蓝牙 BLE 开发实战。