现代 Android 依赖管理:catalog(一)

753 阅读7分钟

传统 Gradle 与 Catalog 对比:现代 Android 依赖管理的演进

基本概述

  • 在 Android 开发的历程中,依赖管理始终是项目构建的核心环节。随着项目规模和复杂度的提升,依赖管理的方式也在不断演进。传统 Gradle 配置在面对大型项目时逐渐力不从心。Gradle 的 Catalog 配置(版本目录)应运而生,其以更先进的管理理念与方式,为现代 Android 开发提供了依赖管理的优秀解决方案。
  • 文章通过介绍传统Gradle 使用时的普遍性问题,进而引出catalog 方案设计的独到之处。文章最后为Compose 项目提供了一套配置范例

一、传统 Gradle 配置:简单直接但问题渐显

历史背景

在 Android 开发早期,项目规模较小,依赖管理相对简单。传统 Gradle 配置直接在 build.gradle 文件中声明依赖,例如:

dependencies {
    implementation 'androidx.core:core-ktx:1.10.0'
    implementation 'androidx.appcompat:appcompat:1.4.0'
}

这种方式在 Gradle 早期版本(如 4.x、5.x)中广泛使用,开发者可以快速添加依赖,无需额外配置,对小型项目友好。

优点

  • 简单直接:上手门槛低,开发者可直接在 build.gradle 中编写依赖,无需学习复杂的配置规则。
  • 即时生效:修改依赖后立即构建,快速看到效果,适合快速验证功能的小型项目。

缺点

  • 版本管理混乱

    • 版本依赖硬编码:需要在不同模块的 build.gradle 中单独指明依赖,
    • 易造成冲突:由于所需特性不一致,则可能导致多个模块引用同一库的不同版本(如 core-ktx 在模块 A 用 1.10.0,模块 B 用 1.12.0),
    • 升级困难 + 极易遗漏:升级时需逐个文件修改
  • 可维护性差

    • 可读性降低:项目规模扩大后,依赖数量增多,build.gradle 文件变得冗长,
    • 无统一配置入口:修改某一依赖的版本或分组时,难以全局把控。
  • 缺乏统一约束

    • 无法保证一组相关库(如 Compose 系列库)的版本兼容性,需开发者自行查阅文档匹配版本,增加出错风险。

二、Catalog 配置:集中管理,解决传统痛点

历史背景与引入原因

Gradle 7.0(2020 年发布)引入 Catalog 配置(版本目录) ,核心目标是解决依赖管理的痛点,提升工程化能力。它通过集中定义依赖版本和元数据,实现依赖的统一管理与语义化引用,让大型项目的依赖管理更高效、清晰。

优点

  • 集中版本管理:在 libs.versions.tomlgradle/libs.versions.toml 中统一定义所有依赖版本,一处修改即可应用到整个项目,避免版本混乱。例如:
[versions]
coreKtx = "1.12.0"
composeBom = "2024.04.00"
  • 语义化引用:通过 libs.xxx 形式引用依赖,如 implementation(libs.androidx.core.ktx),减少拼写错误,提升 build.gradle 的可读性。

  • 强制版本一致性

    • 单一版本数据源:同一依赖在项目中只能有一个版本定义,从源头杜绝版本冲突,
    • BOM(Bill of Materials)机制:可自动管理一组相关库的版本兼容性(如 Compose 库)。
  • 易于维护和升级

    • 升级依赖时只需修改 toml 文件中的版本号,借助 ./gradlew dependencyUpdates 可快速检查过时依赖,提升维护效率。

三、版本兼容情况

  • 传统 Gradle 配置:无严格版本限制,在早期 Gradle 版本(如 4.x - 6.x)中是主流方式,

    • 即便在 Gradle 7.0+ 中也可部分使用,但无法享受集中化管理的优势,复杂项目中问题依旧存在。
  • Catalog 配置:需 Gradle 版本 ≥ 7.0,Android Gradle Plugin(AGP)版本 ≥ 4.1.0(推荐更高版本,如 8.0+,以获得更好的兼容性和功能支持)。

    • 例如,AGP 8.1.4 与 Gradle 8.7+ 配合,能完美支持 Catalog 配置下的 Compose、Room 等库的依赖管理。

四、Catalog 最佳实践

写好 Catalog 配置,需遵循集中管理、语义清晰、合理分组等原则,确保依赖管理高效且易于维护。以下是具体思路与范例:

(一)集中定义所有依赖版本

libs.versions.toml 中统一管理项目涉及的所有依赖版本,避免分散在不同文件。例如:

[versions]
androidGradlePlugin = "8.6.0"
kotlin = "2.0.0"
composeBom = "2024.10.01"  # Compose BOM 版本
coreKtx = "1.12.0"
appcompat = "1.6.1"
material = "1.10.0"
espressoCore = "3.5.1"
room = "2.6.0"
navigation = "2.7.0"
activityCompose = "1.8.0"
lifecycle = "2.6.2"
lifecycleRuntimeComposeAndroid = "2.9.0"

这样,后续所有依赖的版本都从这里引用,一处修改即可全局生效。

(二)语义化命名 + 按功能分组

使用清晰、有意义的名称定义依赖,便于理解和识别。例如:

[libraries]
# AndroidX 基础库
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
androidx-recyclerview = { group = "androidx.recyclerview", name = "recyclerview", version = "1.3.0" }
​
# Material 设计库
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }  # Material3 核心库
google-android-material = { group = "com.google.android.material", name = "material", version.ref = "material" }

通过这种方式,在 build.gradle 中引用时一目了然:

dependencies {
    // AndroidX 基础库
    implementation(libs.androidx.core.ktx)
    implementation(libs.androidx.appcompat)
    implementation(libs.google.android.material)
}

(三)利用平台约束(BOM)

对于像 Compose 这样的库,利用 BOM 统一管理版本,简化依赖声明,确保版本兼容性。如:

[libraries]
# Compose BOM(管理所有 Compose 依赖版本)
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
​
# Compose 核心库(版本由 BOM 管理)
androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation" }
androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout" }  # 布局相关手势
androidx-compose-material = { group = "androidx.compose.material", name = "material" }
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-compose-ui-gesture = { group = "androidx.compose.ui", name = "ui-gesture" }  # 手势交互库

(四)关注版本兼容性与更新

定期检查依赖版本间的兼容性,如 AGP 与 Gradle 版本、库与库之间的适配。同时,利用 ./gradlew dependencyUpdates 命令检查过时依赖,及时更新到稳定的新版本。例如,若 AGP 升级到 8.2.0,可尝试将 Gradle 升级到 9.0+,并确保其他依赖库的版本与之兼容。

五 catalog 版本管理范例

Android Studio 版本信息

image.png

lib.versions.toml

[versions]
androidGradlePlugin = "8.6.0"
kotlin = "2.0.0"
composeBom = "2024.10.01"  # Compose BOM 版本
coreKtx = "1.12.0"
appcompat = "1.6.1"
material = "1.10.0"
espressoCore = "3.5.1"
room = "2.6.0"
navigation = "2.7.0"
activityCompose = "1.8.0"
lifecycle = "2.6.2"
lifecycleRuntimeComposeAndroid = "2.9.0"
​
​
​
[plugins]
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
org-jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
​
[libraries]
# AndroidX 基础库
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
androidx-recyclerview = { group = "androidx.recyclerview", name = "recyclerview", version = "1.3.0" }
​
# Material 设计库
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }  # Material3 核心库
google-android-material = { group = "com.google.android.material", name = "material", version.ref = "material" }
​
# Compose BOM(管理所有 Compose 依赖版本)
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
​
# Compose 核心库(版本由 BOM 管理)
androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation" }
androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout" }  # 布局相关手势
androidx-compose-material = { group = "androidx.compose.material", name = "material" }
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-compose-ui-gesture = { group = "androidx.compose.ui", name = "ui-gesture" }  # 手势交互库# Compose 功能扩展库
androidx-compose-animation = { group = "androidx.compose.animation", name = "animation" }
androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime" }
​
# Compose 开发工具库
androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }  # 开发时预览工具
#androidx-compose-ui-test = { group = "androidx.compose.ui", name = "ui-test" }  # 测试库(已注释)
androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }  # JUnit4 测试支持# Lifecycle 组件
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" }
androidx-lifecycle-runtime-compose-android = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose-android", version.ref = "lifecycleRuntimeComposeAndroid" }  # Compose 生命周期集成# 导航组件
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation" }
​
# Activity 与 Compose 集成
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
​
# Room 数据库组件
androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }  # Kotlin 扩展
androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }  # 运行时库
androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }  # 注解处理器# 测试框架
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }

build.gradle.kts (app)

plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
    alias(libs.plugins.compose.compiler)
    id("kotlin-kapt")
}
​
android {
    namespace = "com.wasbry.nextthing"
    compileSdk = 35
​
    defaultConfig {
        applicationId = "com.wasbry.nextthing"
        minSdk = 24
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"
        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables.useSupportLibrary = true
    }
​
    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
        }
    }
​
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }
​
    kotlinOptions {
        jvmTarget = "17"
    }
​
    buildFeatures {
        compose = true
    }
​
​
    packaging.resources.excludes += "/META-INF/{AL2.0,LGPL2.1}"
​
}
​
dependencies {
    // AndroidX 基础库
    implementation(libs.androidx.core.ktx)
    implementation(libs.androidx.appcompat)
    implementation(libs.google.android.material)
​
    // Compose BOM(管理所有 Compose 依赖版本)
    implementation(platform(libs.androidx.compose.bom))
    androidTestImplementation(platform(libs.androidx.compose.bom))
​
    // Compose 核心库
    implementation(libs.androidx.compose.foundation)
    implementation(libs.androidx.compose.foundation.layout)  // 布局库
    implementation(libs.androidx.compose.material)
    implementation(libs.androidx.compose.ui)
    implementation(libs.androidx.compose.material3)  // Material Design 3 支持// 架构组件
    implementation(libs.androidx.lifecycle.runtime.ktx)  // Lifecycle KTX 扩展
    implementation(libs.androidx.lifecycle.runtime.compose.android)  // Compose 生命周期集成// 导航组件
    implementation(libs.androidx.navigation.compose)  // Compose 导航库// Activity 与 Compose 集成
    implementation(libs.androidx.activity.compose)  // Activity 与 Compose 绑定// Room 数据库
    implementation(libs.androidx.room.ktx)  // Room KTX 扩展
    implementation(libs.androidx.room.runtime)  // Room 运行时库
    kapt(libs.androidx.room.compiler)  // Room 注解处理器// 测试框架
    androidTestImplementation(libs.androidx.espresso.core)  // Espresso 测试核心库
​
}

build.gradle.kts (根目录)

plugins {
    alias(libs.plugins.android.application) apply false
    alias(libs.plugins.kotlin.android) apply false
    alias(libs.plugins.compose.compiler) apply false
}

settings.gradle.kts

pluginManagement {
    repositories {
        gradlePluginPortal()
        maven { url = uri("https://mirrors.cloud.tencent.com/gradle-plugin") }
        maven { url = uri("https://maven.aliyun.com/repository/gradle-plugin") }
        maven { url = uri("https://developer.huawei.com/repo/") }
        maven{ url = uri("https://maven.aliyun.com/nexus/content/groups/public/")  }
        maven{ url = uri("https://maven.aliyun.com/nexus/content/repositories/jcenter")  }
​
        google()
        mavenCentral()
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        maven { url = uri("https://mirrors.cloud.tencent.com/maven") }
        maven { url = uri("https://maven.aliyun.com/repository/public") }
        maven { url = uri("https://developer.huawei.com/repo/") }
​
        maven{ url = uri("https://maven.aliyun.com/nexus/content/groups/public/")  }
        maven{ url = uri("https://maven.aliyun.com/nexus/content/repositories/jcenter")  }
​
        google()
        mavenCentral()
        maven { url = uri("https://jitpack.io") }
    }
}
​
​
rootProject.name = "NextThing"
include(":app")