Gradle Basics & Build System (Android, Java)

Gradle is Android’s build system: it compiles code/resources, merges manifests, processes dependencies, shrinks/obfuscates (R8), signs, and outputs APK or AAB. In this page we learn the essential files, common configuration blocks, and commands you’ll use every day.

Where Gradle Fits

Source

Java/Kotlin + XML + assets

Gradle

Plugins + Tasks + Dependencies

R8/Packaging

S hrink/obfuscate/align/sign

Output

APK (debug/install) or AAB (Play)

Key Files in an Android Project

FilePurpose
settings.gradle / settings.gradle.ktsDeclares which modules are included in the build.
build.gradle (Project)Repositories & plugin management for the whole project.
app/build.gradle (Module)Android plugin + android{} config, dependencies, build types, flavors.
gradle/wrapper/Gradle Wrapper (fixed Gradle version for reproducible builds).
gradle.propertiesGlobal build flags (performance, JVM args, feature toggles).
local.propertiesLocal machine paths (e.g., sdk.dir). Not committed to VCS.

settings.gradle (Single or Multi-Module)

rootProject.name = "MyAndroidApp"
include(":app") // add more like ":core", ":feature-login"

Multi-module projects build faster (isolated changes) and enforce better architecture.

Project-level build.gradle

buildscript {
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        // Android Gradle Plugin (version example)
        classpath 'com.android.tools.build:gradle:8.6.0'
    }
}

Module app/build.gradle Essentials

plugins {
    id 'com.android.application'
    // id 'kotlin-android' if using Kotlin
}

android {
    namespace "com.example.myapp"
    compileSdk 34

    defaultConfig {
        applicationId "com.example.myapp"
        minSdk 21
        targetSdk 34
        versionCode 1
        versionName "1.0"

        // BuildConfig & res values:
        buildConfigField "String", "API_BASE", "\"https://api.example.com\""
        resValue "string", "app_env", "prod"

        // Manifest placeholders (used inside AndroidManifest.xml)
        manifestPlaceholders = [ appAuthRedirectScheme: "myapp" ]
    }

    buildTypes {
        debug {
            applicationIdSuffix ".debug"
            versionNameSuffix "-debug"
            minifyEnabled false
        }
        release {
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            // signingConfig signingConfigs.release  (configured below)
        }
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
    }
}

dependencies {
    implementation 'androidx.appcompat:appcompat:1.7.0'
    implementation 'com.google.android.material:material:1.12.0'

    // Testing
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
}

Dependency Configurations (When to use which)

ConfigurationMeaning
implementationNormal runtime + compile dependency; not exposed to consumers.
api(Library modules) Exposes transitive APIs to consumers.
compileOnlyAvailable at compile-time only (provided at runtime elsewhere).
runtimeOnlyOnly at runtime (not visible to compiler).
testImplementationUnit test dependencies.
androidTestImplementationInstrumentation test dependencies.

Build Types vs Product Flavors

Build Types represent how you build (debug vs release). Flavors represent what you build (free vs paid, dev vs prod URLs).

android {
    flavorDimensions "tier"

    productFlavors {
        dev {
            dimension "tier"
            applicationIdSuffix ".dev"
            versionNameSuffix "-dev"
            buildConfigField "String", "API_BASE", "\"https://dev.api.example.com\""
        }
        prod {
            dimension "tier"
            buildConfigField "String", "API_BASE", "\"https://api.example.com\""
        }
    }
}

Signing Configs (Release)

android {
    signingConfigs {
        release {
            storeFile file("my-release-key.jks")
            storePassword "***"
            keyAlias "mykey"
            keyPassword "***"
        }
    }
    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
    }
}
Security: Keep keystore & passwords out of version control. Use environment variables or Gradle properties.

Gradle Wrapper & Common Commands

CommandWhat it does
./gradlew tasksLists available tasks.
./gradlew assembleDebugBuilds a debug APK.
./gradlew assembleReleaseBuilds a release APK (requires signing).
./gradlew bundleReleaseBuilds a release AAB (Play Store upload).
./gradlew cleanDeletes build outputs.
./gradlew testRuns unit tests.
./gradlew connectedAndroidTestRuns instrumentation tests on device/emulator.
./gradlew dependenciesPrints dependency tree (debugging conflicts).

R8 / ProGuard (Shrinking & Obfuscation)

R8 is enabled for release when minifyEnabled true. Add keep rules in proguard-rules.pro to prevent stripping critical classes (e.g., JSON models used via reflection).

# proguard-rules.pro examples
# Keep model classes (Gson/reflective)
-keep class com.example.myapp.model.** { *; }
# Keep names for JSON fields
-keepattributes Signature,RuntimeVisibleAnnotations,AnnotationDefault

Resource Shrinking

Combine with R8 using shrinkResources true to remove unused resources in release builds.

Performance Tuning (gradle.properties)

# gradle.properties (project)
org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.configureondemand=true
org.gradle.jvmargs=-Xmx4g -Dfile.encoding=UTF-8
# Enable configuration cache (AGP/Gradle version dependent)
org.gradle.configuration-cache=true
Close other heavy apps while building, and keep Android Studio/AGP updated for faster builds.

Multi-Module Setup (Example)

// settings.gradle
include(":app", ":core", ":feature-login")
// feature-login/build.gradle (Android Library)
plugins { id 'com.android.library' }

android {
    namespace "com.example.feature.login"
    compileSdk 34
    defaultConfig { minSdk 21 }
}

dependencies {
    implementation project(':core')
    implementation 'androidx.appcompat:appcompat:1.7.0'
}

Benefits: faster incremental builds, independent testing, clearer boundaries.

Versioning Strategies

  • Keep versionCode increasing (integer). versionName is user-visible (e.g., 1.1.0).
  • You can script version from Git tags or CI using Gradle tasks.
  • Use BOMs (bill of materials) when available to keep library versions aligned.

APK vs AAB

FormatUse
APKInstall directly on devices; great for testing and sharing internal builds.
AABUpload to Play Console; Google generates optimized per-device APKs.

Common Errors & Fixes

  • “Could not find …” → Add/verify google() and mavenCentral() repositories.
  • Dependency version conflict → Run ./gradlew :app:dependencies and align versions/BOM.
  • Dex/Method count → Enable MultiDex for old minSdk (multiDexEnabled true, add dependency).
  • Build slow → Enable configuration cache, parallel builds, increase JVM heap; avoid huge monolithic modules.
  • Release crash only → Check R8 rules; keep classes used via reflection.

End-to-End Flow

Edit code/resources └─> Gradle sync resolves dependencies └─> Build type + flavors select config └─> Compile Java → DEX → Merge resources → Merge manifests └─> R8 shrink/obfuscate (release) → Sign → Output APK/AAB
Tip: Treat Gradle as code: keep it clean, comment tricky parts, and version your build logic just like app code.