Ir al contenido

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

IssueSolution
OOM during buildIncrease org.gradle.jvmargs in gradle.properties
Slow buildsEnable parallel, caching, configuration cache; use --profile
”SDK location not found”Set ANDROID_HOME or create local.properties with sdk.dir
Dependency conflictsUse dependencyInsight task; add exclude or force resolution
”Cannot resolve symbol”Sync Gradle files; invalidate Android Studio caches
Signing config errorsCheck environment variables; verify keystore path and passwords
Lint failures blocking buildUse abortOnError = false or fix lint warnings
Version catalog not resolvingCheck TOML syntax; ensure plugin IDs match exactly
Build cache missesEnable --info flag to see cache miss reasons
AGP version incompatibleCheck Gradle-AGP compatibility table in Android docs