Bỏ qua để đến nội dung

Version Catalog

Version Catalog là cách quản lý versions của dependencies và plugins tập trung trong một file duy nhất. Thay vì khai báo version scattered khắp các build files, bạn định nghĩa tất cả trong libs.versions.toml.

// ❌ Cách cũ: Version hardcode trong build.gradle.kts
dependencies {
implementation("androidx.core:core-ktx:1.15.0")
implementation("androidx.compose.ui:ui:1.7.5")
implementation("com.squareup.retrofit2:retrofit:2.9.0")
}

Nhược điểm:

  • Khó maintain khi có nhiều modules
  • Dễ conflict versions giữa các modules
  • Không có IDE auto-complete
  • Khó tìm để cập nhật
// ✅ Version Catalog: Type-safe references
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.ui)
implementation(libs.retrofit)
}

Ưu điểm:

  • ✅ Quản lý versions tập trung
  • ✅ IDE auto-complete và navigation
  • ✅ Type-safe (báo lỗi compile-time)
  • ✅ Dễ dàng cập nhật versions
  • ✅ Tái sử dụng across modules

File Version Catalog đặt tại gradle/libs.versions.toml và có 3 sections chính:

flowchart TB
subgraph TOML["libs.versions.toml"]
V["[versions]<br/>kotlin = 2.0.21<br/>agp = 8.7.2"]
L["[libraries]<br/>androidx-core-ktx<br/>retrofit"]
P["[plugins]<br/>android-application<br/>kotlin-android"]
B["[bundles]<br/>compose<br/>retrofit"]
end
V --> L
V --> P
L --> B
L --> BG["build.gradle.kts<br/>libs.androidx.core.ktx"]
P --> BG2["build.gradle.kts<br/>libs.plugins.android.application"]
style V fill:#e3f2fd
style L fill:#e8f5e9
style P fill:#fff3e0
style B fill:#fce4ec
[versions]
# Định nghĩa version numbers
[libraries]
# Định nghĩa dependencies
[plugins]
# Định nghĩa Gradle plugins
[bundles]
# (Optional) Nhóm các libraries

Định nghĩa biến chứa version numbers:

[versions]
kotlin = "2.0.21"
agp = "8.7.2"
compose-bom = "2024.11.00"
core-ktx = "1.15.0"
lifecycle = "2.8.7"
activity-compose = "1.9.3"
navigation = "2.8.4"
room = "2.6.1"
retrofit = "2.11.0"
hilt = "2.52"
ksp = "2.0.21-1.0.27"
junit = "4.13.2"

💡 Mẹo: Naming convention: Sử dụng kebab-case (dấu gạch ngang) cho tên biến. Ví dụ: compose-bom, core-ktx.


Định nghĩa dependencies với group, name, và version.ref:

[libraries]
# Cách 1: Full format
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
# Cách 2: Compact format (module notation)
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
# Cách 3: Version inline (không khuyến khích)
junit = { module = "junit:junit", version = "4.13.2" }
# Libraries không có version (dùng với BOM)
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
dependencies {
// libs.{library-alias} - dấu gạch ngang thành dấu chấm
implementation(libs.androidx.core.ktx)
implementation(libs.retrofit)
testImplementation(libs.junit)
}

📝 Lưu ý: Alias androidx-core-ktx trong TOML trở thành libs.androidx.core.ktx trong Kotlin DSL (dấu - thành .)


Định nghĩa Gradle plugins:

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
android-library = { id = "com.android.library", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
room = { id = "androidx.room", version.ref = "room" }
// build.gradle.kts (Project-level)
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
}
// build.gradle.kts (Module-level)
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.hilt)
}

Nhóm nhiều libraries thường đi cùng nhau:

[bundles]
compose = ["androidx-ui", "androidx-ui-graphics", "androidx-ui-tooling-preview", "androidx-material3"]
retrofit = ["retrofit", "retrofit-gson", "okhttp-logging"]
room = ["room-runtime", "room-ktx"]
dependencies {
implementation(libs.bundles.compose)
implementation(libs.bundles.retrofit)
}

BOM là một dependency đặc biệt quản lý versions của một họ libraries. Khi sử dụng BOM, bạn không cần khai báo version cho từng library trong họ đó.

[versions]
compose-bom = "2024.11.00"
[libraries]
# BOM
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
# Libraries trong BOM - KHÔNG cần version
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
dependencies {
// Import BOM - quyết định versions
implementation(platform(libs.androidx.compose.bom))
// Các libraries không cần version
implementation(libs.androidx.ui)
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.material3)
debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.tooling.preview)
}

⚠️ Quan trọng: Khi dùng BOM, KHÔNG khai báo version cho các libraries thuộc BOM đó. BOM sẽ tự động cung cấp compatible versions.


[versions]
# Core
kotlin = "2.0.21"
agp = "8.7.2"
ksp = "2.0.21-1.0.27"
# AndroidX
core-ktx = "1.15.0"
lifecycle = "2.8.7"
activity-compose = "1.9.3"
navigation = "2.8.4"
# Compose
compose-bom = "2024.11.00"
# Database
room = "2.6.1"
datastore = "1.1.1"
# Network
retrofit = "2.11.0"
okhttp = "4.12.0"
kotlinx-serialization = "1.7.3"
# DI
hilt = "2.52"
hilt-navigation-compose = "1.2.0"
# Image
coil = "2.7.0"
# Testing
junit = "4.13.2"
androidx-junit = "1.2.1"
espresso = "3.6.1"
[libraries]
# Core
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" }
androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activity-compose" }
# Compose BOM
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
# Navigation
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation" }
# Room
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
# DataStore
datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastore" }
# Network
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
retrofit-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "retrofit" }
okhttp-logging = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
# DI
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt" }
hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "hilt-navigation-compose" }
# Image
coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
# Testing
junit = { module = "junit:junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-junit" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso" }
[bundles]
compose = ["androidx-ui", "androidx-ui-graphics", "androidx-ui-tooling-preview", "androidx-material3"]
compose-debug = ["androidx-ui-tooling"]
lifecycle = ["androidx-lifecycle-runtime-ktx", "androidx-lifecycle-viewmodel-compose"]
room = ["room-runtime", "room-ktx"]
retrofit = ["retrofit", "retrofit-gson", "okhttp-logging"]
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
android-library = { id = "com.android.library", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
room = { id = "androidx.room", version.ref = "room" }

Nếu project hiện tại đang dùng string dependencies, migrate theo các bước:

Terminal window
# Tạo file trong thư mục gradle/
touch gradle/libs.versions.toml

Trước:

dependencies {
implementation("androidx.core:core-ktx:1.15.0")
}

Sau - Trong libs.versions.toml:

[versions]
core-ktx = "1.15.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }

Sau - Trong build.gradle.kts:

dependencies {
implementation(libs.androidx.core.ktx)
}

Trước:

plugins {
id("com.android.application") version "8.7.2" apply false
}

Sau:

libs.versions.toml
[versions]
agp = "8.7.2"
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
build.gradle.kts
plugins {
alias(libs.plugins.android.application) apply false
}

💡 Mẹo: Migrate dần dần, một dependency/plugin mỗi lần. Build có thể consume cả hai cách đồng thời.


# ✅ Tốt: kebab-case
androidx-core-ktx = { ... }
compose-bom = "2024.11.00"
# ❌ Tránh: camelCase hoặc snake_case
androidxCoreKtx = { ... }
compose_bom = "2024.11.00"
[versions]
# Group related libraries
kotlin = "2.0.21"
ksp = "2.0.21-1.0.27" # KSP tied to Kotlin version
compose-bom = "2024.11.00"
room = "2.6.1"
[libraries]
# Core AndroidX
androidx-core-ktx = { ... }
androidx-activity-compose = { ... }
# Compose UI
androidx-compose-bom = { ... }
androidx-ui = { ... }
# Database
room-runtime = { ... }