Gradle Android Cheat Sheet
Overview
Gradle is the official build system for Android applications, integrated into Android Studio and used by virtually all Android projects. It automates the process of compiling source code, packaging resources, generating APK/AAB files, running tests, and managing dependencies. Android projects use Gradle with the Android Gradle Plugin (AGP), which provides Android-specific build tasks, product flavors for building multiple app variants, build types for debug/release configurations, and integration with the Android SDK.
Modern Android projects use Kotlin DSL (.gradle.kts files) for build configuration, offering better IDE support, type safety, and auto-completion compared to the traditional Groovy DSL. Gradle supports incremental builds, build caching, and configuration caching for faster build times. The build system handles complex scenarios like multi-module projects, dynamic feature modules, library publishing, and CI/CD integration with features like dependency verification and reproducible builds.
Installation
# Gradle is bundled with Android Studio
# For standalone use:
# macOS
brew install gradle
# Linux (SDKMAN - recommended)
curl -s "https://get.sdkman.io" | bash
sdk install gradle
# Verify installation
gradle --version
./gradlew --version # Project wrapper (preferred)
# Update Gradle wrapper
./gradlew wrapper --gradle-version=8.7
# Android SDK setup
# Set ANDROID_HOME
export ANDROID_HOME=$HOME/Android/Sdk
export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin
export PATH=$PATH:$ANDROID_HOME/platform-tools
# Accept SDK licenses
sdkmanager --licenses
Project Structure
MyAndroidApp/
├── build.gradle.kts # Root build file
├── settings.gradle.kts # Project settings
├── gradle.properties # Gradle properties
├── gradle/
│ ├── wrapper/
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
│ └── libs.versions.toml # Version catalog
├── app/ # App module
│ ├── build.gradle.kts # App build config
│ ├── proguard-rules.pro # R8/ProGuard rules
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ ├── kotlin/
│ │ └── res/
│ ├── debug/ # Debug-specific sources
│ ├── release/ # Release-specific sources
│ └── test/ # Unit tests
│ └── java/
├── library/ # Library module
│ └── build.gradle.kts
└── buildSrc/ # Custom build logic
└── src/main/kotlin/
Root Build File
// build.gradle.kts (root)
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.android.library) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.kotlin.compose) apply false
alias(libs.plugins.hilt) apply false
alias(libs.plugins.ksp) apply false
}
// Clean task
tasks.register("clean", Delete::class) {
delete(rootProject.layout.buildDirectory)
}
App Build File
// app/build.gradle.kts
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.hilt)
alias(libs.plugins.ksp)
}
android {
namespace = "com.example.myapp"
compileSdk = 35
defaultConfig {
applicationId = "com.example.myapp"
minSdk = 24
targetSdk = 35
versionCode = 1
versionName = "1.0.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true
}
// Build config fields
buildConfigField("String", "API_URL", "\"https://api.example.com\"")
buildConfigField("Boolean", "ENABLE_LOGGING", "false")
}
signingConfigs {
create("release") {
storeFile = file(System.getenv("KEYSTORE_PATH") ?: "release.keystore")
storePassword = System.getenv("KEYSTORE_PASSWORD") ?: ""
keyAlias = System.getenv("KEY_ALIAS") ?: ""
keyPassword = System.getenv("KEY_PASSWORD") ?: ""
}
}
buildTypes {
debug {
isDebuggable = true
isMinifyEnabled = false
applicationIdSuffix = ".debug"
versionNameSuffix = "-debug"
buildConfigField("String", "API_URL", "\"https://api-dev.example.com\"")
buildConfigField("Boolean", "ENABLE_LOGGING", "true")
}
release {
isMinifyEnabled = true
isShrinkResources = true
signingConfig = signingConfigs.getByName("release")
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
flavorDimensions += "version"
productFlavors {
create("free") {
dimension = "version"
applicationIdSuffix = ".free"
versionNameSuffix = "-free"
buildConfigField("Boolean", "IS_PREMIUM", "false")
}
create("premium") {
dimension = "version"
buildConfigField("Boolean", "IS_PREMIUM", "true")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
buildFeatures {
compose = true
buildConfig = true
viewBinding = true
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
lint {
abortOnError = false
warningsAsErrors = true
checkDependencies = true
htmlReport = true
}
}
dependencies {
// Version catalog references
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.material3)
// Networking
implementation(libs.retrofit)
implementation(libs.okhttp)
// DI
implementation(libs.hilt.android)
ksp(libs.hilt.compiler)
// Testing
testImplementation(libs.junit)
testImplementation(libs.mockk)
androidTestImplementation(libs.androidx.test.ext)
androidTestImplementation(libs.androidx.test.espresso)
// Debug only
debugImplementation(libs.leakcanary)
}
Version Catalog
# gradle/libs.versions.toml
[versions]
agp = "8.7.0"
kotlin = "2.0.20"
compose-bom = "2025.01.00"
hilt = "2.52"
retrofit = "2.11.0"
okhttp = "4.12.0"
coroutines = "1.9.0"
room = "2.6.1"
ksp = "2.0.20-1.0.25"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version = "1.15.0" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version = "2.8.7" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version = "1.9.3" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt" }
junit = { group = "junit", name = "junit", version = "4.13.2" }
mockk = { group = "io.mockk", name = "mockk", version = "1.13.13" }
[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" }
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
Common Tasks
# Build
./gradlew assembleDebug # Build debug APK
./gradlew assembleRelease # Build release APK
./gradlew bundleRelease # Build release AAB (for Play Store)
./gradlew assembleFreeDebug # Build specific variant
./gradlew installDebug # Build and install on device
# Test
./gradlew test # Run unit tests
./gradlew testDebugUnitTest # Run debug unit tests
./gradlew connectedAndroidTest # Run instrumented tests
./gradlew jacocoTestReport # Generate coverage report
# Clean
./gradlew clean # Clean build outputs
./gradlew cleanBuildCache # Clean build cache
# Info
./gradlew tasks # List all tasks
./gradlew tasks --group=build # List build tasks
./gradlew dependencies # Show dependency tree
./gradlew app:dependencies # Module-specific deps
./gradlew app:dependencies --configuration releaseRuntimeClasspath
./gradlew dependencyInsight --dependency okhttp # Specific dep info
# Analysis
./gradlew lint # Run Android lint
./gradlew lintDebug # Lint specific variant
./gradlew signingReport # Show signing info
# Performance
./gradlew assembleDebug --profile # Generate build profile
./gradlew assembleDebug --scan # Gradle build scan
./gradlew --build-cache assembleDebug # Use build cache
Gradle Properties
# gradle.properties
# JVM memory
org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError
# Parallel execution
org.gradle.parallel=true
# Configuration cache
org.gradle.configuration-cache=true
# Build cache
org.gradle.caching=true
# Kotlin
kotlin.code.style=official
kotlin.incremental=true
# Android
android.useAndroidX=true
android.nonTransitiveRClass=true
# Disable unused features
android.defaults.buildfeatures.aidl=false
android.defaults.buildfeatures.renderscript=false
ProGuard / R8 Rules
# proguard-rules.pro
# Keep data classes for serialization
-keep class com.example.myapp.data.model.** { *; }
# Retrofit
-keepattributes Signature
-keepattributes *Annotation*
-keep class retrofit2.** { *; }
-keepclasseswithmembers class * {
@retrofit2.http.* <methods>;
}
# Gson / Moshi
-keep class com.google.gson.** { *; }
-keepclassmembers class * {
@com.google.gson.annotations.SerializedName <fields>;
}
# Coroutines
-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {}
-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {}
# Debugging
-keepattributes SourceFile,LineNumberTable
-renamesourcefileattribute SourceFile
Advanced Usage
// Custom task
tasks.register("printVersionName") {
doLast {
println("Version: ${android.defaultConfig.versionName}")
}
}
// Dynamic version from git
fun getGitVersionCode(): Int {
return try {
val process = Runtime.getRuntime().exec("git rev-list HEAD --count")
process.inputStream.bufferedReader().readText().trim().toInt()
} catch (e: Exception) { 1 }
}
fun getGitVersionName(): String {
return try {
val process = Runtime.getRuntime().exec("git describe --tags --abbrev=0")
process.inputStream.bufferedReader().readText().trim()
} catch (e: Exception) { "1.0.0" }
}
android {
defaultConfig {
versionCode = getGitVersionCode()
versionName = getGitVersionName()
}
}
// Multi-module setup
// settings.gradle.kts
include(":app")
include(":core:common")
include(":core:network")
include(":core:database")
include(":feature:home")
include(":feature:profile")
// Convention plugins (buildSrc)
// buildSrc/src/main/kotlin/AndroidLibraryConventionPlugin.kt
class AndroidLibraryConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
pluginManager.apply("com.android.library")
pluginManager.apply("org.jetbrains.kotlin.android")
extensions.configure<LibraryExtension> {
compileSdk = 35
defaultConfig { minSdk = 24 }
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
}
}
}
}
Troubleshooting
| Issue | Solution |
|---|---|
| OOM during build | Increase org.gradle.jvmargs in gradle.properties |
| Slow builds | Enable parallel, caching, configuration cache; use --profile |
| ”SDK location not found” | Set ANDROID_HOME or create local.properties with sdk.dir |
| Dependency conflicts | Use dependencyInsight task; add exclude or force resolution |
| ”Cannot resolve symbol” | Sync Gradle files; invalidate Android Studio caches |
| Signing config errors | Check environment variables; verify keystore path and passwords |
| Lint failures blocking build | Use abortOnError = false or fix lint warnings |
| Version catalog not resolving | Check TOML syntax; ensure plugin IDs match exactly |
| Build cache misses | Enable --info flag to see cache miss reasons |
| AGP version incompatible | Check Gradle-AGP compatibility table in Android docs |