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.
Tại sao cần Version Catalog?
Vấn đề với cách cũ
// ❌ 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
Giải pháp: Version Catalog
// ✅ 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
Cấu trúc file libs.versions.toml
File Version Catalog đặt tại gradle/libs.versions.toml và có 3 sections chính:
[versions]
# Định nghĩa version numbers
[libraries]
# Định nghĩa dependencies
[plugins]
# Định nghĩa Gradle plugins
[bundles]
# (Optional) Nhóm các librariesSection [versions]
Đị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.
Section [libraries]
Đị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" }Sử dụng trong build.gradle.kts
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-ktxtrong TOML trở thànhlibs.androidx.core.ktxtrong Kotlin DSL (dấu-thành.)
Section [plugins]
Đị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" }Sử dụng trong build files
// 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)
}Section [bundles] (Optional)
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"]Sử dụng bundle
dependencies {
implementation(libs.bundles.compose)
implementation(libs.bundles.retrofit)
}Bill of Materials (BOM)
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ọ đó.
Khai báo BOM
[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" }Sử dụng BOM
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.
Ví dụ hoàn chỉnh
libs.versions.toml
[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" }Migration từ String Dependencies
Nếu project hiện tại đang dùng string dependencies, migrate theo các bước:
Bước 1: Tạo file libs.versions.toml
# Tạo file trong thư mục gradle/
touch gradle/libs.versions.tomlBước 2: Migrate từng dependency
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)
}Bước 3: Migrate plugins
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.
Best Practices
1. Naming Convention
# ✅ 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"2. Grouped Versions
[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"3. Comment Sections
[libraries]
# Core AndroidX
androidx-core-ktx = { ... }
androidx-activity-compose = { ... }
# Compose UI
androidx-compose-bom = { ... }
androidx-ui = { ... }
# Database
room-runtime = { ... }