Version Catalog:统一版本管理的新时代

说实话,在接触 Version Catalog 之前,我管理 Gradle 依赖的方式还挺原始的。就是那种在根项目的 build.gradle 里定义一堆 ext 变量,然后各个模块引用。项目小的时候还好,一旦模块多了,维护起来简直是一场噩梦。

我记得有一次,团队里两个同事同时升级了同一个库的不同版本,结果编译报错查了半天。从那以后,我就下定决心要找一个更靠谱的方案。嗯,Version Catalog 就是那个答案。

什么是 Version Catalog?

说白了,Version Catalog 是 Gradle 7.0 引入的一个特性。它让你用一个统一的 libs.versions.toml 文件来管理所有依赖的版本号、库声明和插件版本。

你想想看,以前我们是怎么做的?

// 老方式:ext 变量
ext {
    compose_version = '1.3.0'
    retrofit_version = '2.9.0'
    okhttp_version = '4.10.0'
}

// 然后在各个模块里引用
implementation "androidx.compose.ui:compose-ui:$compose_version"
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"

这种方式有什么问题?变量名容易冲突,IDE 提示不友好,而且你没法快速看到某个库的所有引用位置。

用 Version Catalog 之后,画风就变了:

// libs.versions.toml
[versions]
compose = "1.3.0"
retrofit = "2.9.0"

[libraries]
compose-ui = { group = "androidx.compose.ui", name = "compose-ui", version.ref = "compose" }
retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }

// 在 build.gradle.kts 里引用
implementation(libs.compose.ui)
implementation(libs.retrofit)

看到区别了吗?类型安全、自动补全、集中管理,这才是现代 Android 项目该有的样子。

libs.versions.toml 文件结构

这个文件通常放在 gradle/ 目录下。我习惯把它拆成三个主要区块:

区块 作用 示例
[versions] 定义版本号变量 kotlin = "1.8.0"
[libraries] 声明库依赖 kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
[plugins] 声明 Gradle 插件 kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
我的习惯:版本号统一放在 [versions] 里,库声明用 version.ref 引用。这样改一个版本号,所有引用自动更新,不会出现漏改的情况。

声明与使用版本目录

先看一个完整的 libs.versions.toml 示例:

[versions]
agp = "8.1.0"
kotlin = "1.8.20"
compose-bom = "2023.05.01"
compose-compiler = "1.4.6"
retrofit = "2.9.0"
okhttp = "4.11.0"
coroutines = "1.7.1"

[libraries]
# BOM
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
compose-ui = { group = "androidx.compose.ui", name = "compose-ui" }
compose-material3 = { group = "androidx.compose.material3", name = "material3" }

# 网络
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" }

# Kotlin
kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

然后在模块的 build.gradle.kts 里这样用:

plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
}

android {
    // ...
}

dependencies {
    // Compose
    implementation(platform(libs.compose.bom))
    implementation(libs.compose.ui)
    implementation(libs.compose.material3)

    // 网络
    implementation(libs.retrofit.core)
    implementation(libs.retrofit.gson)
    implementation(libs.okhttp.core)
    implementation(libs.okhttp.logging)

    // 协程
    implementation(libs.kotlinx.coroutines)
}
关键点:Gradle 会自动根据 toml 文件生成 libs 扩展对象。库名中的连字符会被转为点号,比如 compose-ui 变成 libs.compose.ui

多模块统一版本管理

这才是 Version Catalog 真正发光的地方。我参与过一个 20+ 模块的项目,每个模块都有自己的依赖。以前最怕的就是版本不一致,比如 moduleA 用了 Retrofit 2.9.0,moduleB 用了 2.8.0,编译时没问题,运行时却出了诡异 bug。

有了 Version Catalog,所有模块都从同一个地方读取版本:

// 所有模块的 build.gradle.kts 都这样写
dependencies {
    implementation(libs.retrofit.core)  // 版本统一由 toml 控制
    implementation(libs.okhttp.core)
}

想升级 Retrofit?只需要改 toml 文件里的版本号,然后重新编译。所有模块自动同步,不会遗漏。

我曾经踩过的坑:千万别在模块里直接写死版本号!比如 implementation("com.squareup.retrofit2:retrofit:2.9.0")。这样 Version Catalog 就形同虚设了。所有依赖必须通过 libs 引用。

目录的共享与发布

如果你的公司有多个项目,或者你想把公共依赖库分享给团队,Version Catalog 支持发布到 Maven 仓库。

具体做法是创建一个独立的 Gradle 项目,里面只放 toml 文件,然后发布:

// 在独立的 catalog 项目中
plugins {
    `version-catalog`
    `maven-publish`
}

catalog {
    versionCatalog {
        from(files("gradle/libs.versions.toml"))
    }
}

publishing {
    publications {
        create<MavenPublication>("catalog") {
            from(components["versionCatalog"])
        }
    }
}

其他项目引用时,在 settings.gradle.kts 里配置:

dependencyResolutionManagement {
    repositories {
        mavenCentral()
        // 公司私有仓库
        maven { url = uri("https://your-company.com/repo") }
    }
    versionCatalogs {
        create("libs") {
            from("com.yourcompany:catalog:1.0.0")
        }
    }
}

这样,所有项目都能共享同一套依赖版本标准。新项目初始化时,只需要引入 catalog 坐标,不用再手动写一堆依赖声明。

知识体系总览

下面这张图帮你理清 Version Catalog 的核心脉络:

Version Catalog 核心架构 libs.versions.toml [versions] 版本号定义 agp = "8.1.0" [libraries] 库依赖声明 retrofit = { module: ... } [plugins] 插件版本声明 android-application = ... 单项目使用 直接引用 libs.xxx 多模块统一 所有模块共享版本 发布共享 Maven 仓库分发 统一声明 → 类型安全引用 → 集中管理 → 轻松升级

最佳实践与避坑指南

  • 命名规范要统一:我习惯用 库名-功能 的格式,比如 retrofit-coreokhttp-logging。这样在 IDE 里输入 libs. 后,自动补全列表一目了然。
  • BOM 要特殊处理:Compose 的 BOM 需要用 platform() 包裹,否则版本可能不生效。我见过有人直接 implementation(libs.compose.bom),结果版本没对齐,UI 渲染异常。
  • 不要混用新旧方式:既然用了 Version Catalog,就彻底放弃 ext 变量。混用只会让维护更混乱。
  • 定期清理无用依赖:项目迭代过程中,有些库可能不再使用了。记得及时从 toml 文件里移除,保持目录干净。
小技巧:在 Android Studio 里,输入 libs. 后按 Ctrl+Space,可以看到所有可用的库。如果发现某个库没出现,检查一下 toml 文件的语法是否正确。

说实话,Version Catalog 是我近几年在 Gradle 构建中最喜欢的特性之一。它让依赖管理变得清晰、安全、可维护。如果你还在用 ext 变量管理版本,我建议你尽快迁移过来。刚开始可能会有点不习惯,但用上一周后,你就再也回不去了。


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