| name | gradle-configuration |
| description | Configures Gradle builds for Android using Version Catalogs and Kotlin DSL. Use when user asks to "add a dependency", "configure Gradle", "update a library version", "set up a version catalog", or "resolve a build conflict". |
Gradle Configuration
Overview
Best practices for configuring Android Gradle builds: Version Catalogs as single source of truth, Kotlin DSL, dependency management, build performance, and common troubleshooting.
Version Catalog (libs.versions.toml)
Single source of truth for all dependency versions. Located at gradle/libs.versions.toml.
Structure
[versions]
kotlin = "2.1.21"
agp = "8.5.0"
compose-bom = "2024.12.01"
retrofit = "2.11.0"
mockk = "1.13.12"
coroutines = "1.8.1"
[libraries]
retrofit-core = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
retrofit-converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "retrofit" }
compose-bom = { module = "androidx.compose:compose-bom", version.ref = "compose-bom" }
compose-ui = { module = "androidx.compose.ui:ui" }
compose-material3 = { module = "androidx.compose.material3:material3" }
mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" }
[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" }
[bundles]
retrofit = ["retrofit-core", "retrofit-converter-gson"]
compose-core = ["compose-ui", "compose-material3"]
Usage in build.gradle.kts
dependencies {
implementation(libs.retrofit.core)
implementation(libs.retrofit.converter.gson)
implementation(libs.bundles.retrofit)
implementation(platform(libs.compose.bom))
implementation(libs.compose.ui)
implementation(libs.compose.material3)
testImplementation(libs.mockk)
testImplementation(libs.kotlinx.coroutines.test)
}
Module build.gradle.kts
Application Module
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
id("my.android.application")
}
android {
namespace = "com.example.myapp"
}
dependencies {
implementation(project(":feature:home"))
implementation(project(":feature:profile"))
implementation(libs.androidx.core.ktx)
}
Library Module
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
id("my.android.library")
id("my.android.unit.test")
}
android {
namespace = "com.example.feature.home"
}
dependencies {
implementation(project(":component:user"))
implementation(libs.androidx.lifecycle.viewmodel)
}
Dependency Scopes
| Scope | When to Use |
|---|
implementation | Internal dependency, not leaked to consumers |
api | Dependency exposed to consumers (use sparingly) |
compileOnly | Needed only at compile time (annotations, Lint rules) |
runtimeOnly | Needed only at runtime |
testImplementation | Unit test only |
androidTestImplementation | Instrumented test only |
debugImplementation | Debug build only (e.g., LeakCanary, Compose tooling) |
Prefer implementation over api. api leaks transitive dependencies, increases coupling, and slows compilation.
Dependency Conflict Resolution
Force a Version
configurations.all {
resolutionStrategy {
force(libs.kotlin.stdlib)
eachDependency {
if (requested.group == "org.jetbrains.kotlin") {
useVersion(libs.versions.kotlin.get())
because("All Kotlin modules must use the same version")
}
}
}
}
Exclude a Transitive Dependency
implementation(libs.someLibrary) {
exclude(group = "com.google.guava", module = "guava")
}
View Dependency Tree
./gradlew :app:dependencies --configuration releaseRuntimeClasspath
./gradlew :feature:home:dependencies
Build Performance
gradle.properties
# Parallel execution
org.gradle.parallel=true
# Build cache
org.gradle.caching=true
# Configuration cache (Gradle 8+)
org.gradle.configuration-cache=true
# Increase heap for large projects
org.gradle.jvmargs=-Xmx4g -XX:+UseParallelGC
# AndroidX
android.useAndroidX=true
# R8 in debug (optional — faster debug builds)
android.enableR8.fullMode=false
Configuration Cache
./gradlew assembleDebug
./gradlew assembleDebug
Incompatible tasks must be excluded:
org.gradle.configuration-cache.problems=warn
Compatibility Matrix (Critical)
Certain dependencies must align or builds fail:
| Dependency | Constraint |
|---|
| Kotlin ↔ Compose Compiler | Must match exactly per release |
| AGP ↔ Gradle | AGP requires minimum Gradle version |
| AGP ↔ Kotlin | Minimum Kotlin version per AGP version |
Check before upgrading:
Common Gradle Commands
./gradlew assembleDebug
./gradlew assembleRelease
./gradlew test
./gradlew :feature:home:test
./gradlew lint
./gradlew :feature:home:lint
./gradlew clean
./gradlew dependencyInsight --dependency retrofit --configuration runtimeClasspath
./gradlew dependencyUpdates
./gradlew build --scan
Kotlin DSL Tips
val kotlinVersion = libs.versions.kotlin.get()
android {
buildTypes {
release {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
)
}
debug {
applicationIdSuffix = ".debug"
isDebuggable = true
}
}
}
android {
flavorDimensions += "environment"
productFlavors {
create("dev") {
dimension = "environment"
applicationIdSuffix = ".dev"
buildConfigField("String", "BASE_URL", "\"https://dev.api.example.com\"")
}
create("prod") {
dimension = "environment"
buildConfigField("String", "BASE_URL", "\"https://api.example.com\"")
}
}
}
Checklist: Adding a Dependency
References