Skip to content
All posts
March 21, 20264 min read

Gradle Build Optimization: Cut Your Android Build Times in Half

A slow Gradle build kills developer productivity. Every wasted build minute multiplies across your team and across your day. Here's how to diagnose and fix the most common Android build performance problems.

AndroidToolsPerformanceEngineering
Share:

A 5-minute build run 20 times a day costs 100 minutes. In a week, that's over 8 hours. Build performance is not a nice-to-have.

Here's how to find what's slow and fix it.


Step 1: Profile Your Build

You can't optimize what you don't measure. Run a profiled build:

bash
./gradlew assembleDebug --profile --scan

code
--profile
generates an HTML report at
code
build/reports/profile/
.

code
--scan
(requires acceptance of terms) gives a detailed build scan at scans.gradle.com with task-level timing. Most informative option.

Look for:

  • Which tasks take the most time
  • Which modules take the most time
  • Whether annotation processing is slow
  • Which tasks are NOT being cached (cache miss indicators)

Step 2: Enable Gradle Caching

The biggest quick win for most Android projects:

kotlin
// gradle.properties
org.gradle.caching=true
org.gradle.configuration-cache=true

Build cache: Gradle stores outputs of tasks. If the inputs haven't changed (source files, config), the cached output is used instead of rerunning the task.

code
./gradlew assembleDebug
the second time can be 3-5x faster.

Configuration cache: Stores the build configuration phase. When your build scripts and settings haven't changed, Gradle skips reconfiguration entirely. Can save 10-30 seconds on large projects.

bash
# Check if your build is cache-compatible
./gradlew help --configuration-cache

Step 3: Increase Gradle Heap

Gradle defaults to a small heap. Android builds need more:

kotlin
// gradle.properties
org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -XX:+UseG1GC

code
-Xmx4g
gives Gradle 4GB. For large projects, 6-8GB. The G1 garbage collector reduces GC pauses.

Too little heap means constant GC pressure and slower builds.


Step 4: Enable Parallel Builds

Multi-module projects can build modules in parallel:

kotlin
// gradle.properties
org.gradle.parallel=true

This requires that your modules have correct dependency declarations — Gradle needs to know which modules depend on which to parallelize safely. If modules have incorrect dependencies, you'll get flaky builds. Most well-structured projects handle this fine.


Step 5: Use Kotlin Daemon

The Kotlin compiler daemon stays running between builds to avoid JVM startup overhead:

kotlin
// gradle.properties — already enabled in modern projects
kotlin.daemon.jvm.options=-Xmx2g

Set the daemon heap to 2GB for large projects. Too small causes GC pressure in the compiler.


Step 6: Switch to KSP From KAPT

KAPT (Kotlin Annotation Processing Tool) runs in a separate JVM process. KSP (Kotlin Symbol Processing) runs in the compiler directly and is 2-3x faster:

kotlin
// Before: KAPT
plugins {
    id("kotlin-kapt")
}
dependencies {
    kapt("com.google.dagger:hilt-android-compiler:2.51")
    kapt("androidx.room:room-compiler:2.6.1")
}

// After: KSP (check library docs for KSP support)
plugins {
    id("com.google.devtools.ksp") version "2.1.20-1.0.29"
}
dependencies {
    ksp("com.google.dagger:hilt-android-compiler:2.51")
    ksp("androidx.room:room-compiler:2.6.1")
}

Hilt, Room, Moshi, Retrofit all support KSP. The switch alone can save 30-60 seconds per build on annotation-heavy projects.


Step 7: Incremental Builds

Incremental compilation only recompiles changed files. This is enabled by default in Kotlin/Gradle but can be inadvertently broken:

kotlin
// gradle.properties
kotlin.incremental=true
kotlin.incremental.android=true

What breaks incremental compilation:

  • Annotation processors that don't support incremental mode
  • Build scripts that always clean before building
  • Modifying build scripts too frequently (invalidates configuration cache)

Step 8: Module Structure for Better Parallelism

If you have a monolithic app module, consider splitting it:

code
:app (thin layer — Activity, navigation)
  ├── :feature:tasks (task list, detail screens)
  ├── :feature:settings (settings screens)
  ├── :core:data (repositories, database)
  ├── :core:network (API client)
  └── :core:ui (shared components)

Benefits:

  • Modules build in parallel
  • Changing
    code
    :feature:settings
    doesn't require recompiling
    code
    :feature:tasks
  • Build cache efficiency improves (unchanged modules are always from cache)

The Optimization Stack (In Order of Impact)

OptimizationEffortImpact
Gradle build cacheLowHigh
KSP instead of KAPTLow-MediumHigh
Increase JVM heapLowMedium
Configuration cacheLowMedium
Parallel buildsLowMedium
Module splittingHighHigh (for large projects)

Start with the low-effort, high-impact items. Measure after each change.


Checking Your Baseline

Before and after optimization:

bash
# Clean build (measures worst case)
./gradlew clean assembleDebug
# Note time

# Incremental build (change one file)
touch app/src/main/java/com/yourapp/MainActivity.kt
./gradlew assembleDebug
# Note time — should be much faster

Target: clean build under 2 minutes for small apps, under 5 minutes for large apps. Incremental build under 30 seconds.


Takeaways

  • Profile before optimizing —
    code
    --scan
    shows exactly which tasks are slow
  • Build cache + configuration cache is the highest-ROI change, takes 2 minutes to enable
  • KSP instead of KAPT saves 30-60 seconds per build on annotation-heavy projects
  • Increase Gradle and Kotlin daemon heap — memory pressure is a common hidden bottleneck
  • Module splitting has the highest long-term impact but takes the most work
Share:
S

Sudarshan Chaudhari

AI Systems Builder / Product Engineer

Bangkok, Thailand

Solo Android developer with 13+ years in QA, building Android apps, AI automation systems, and developer tools at SudarshanTechLabs.

Stay updated

Get new posts on Android, Kotlin, and solo dev straight to your inbox.

Newsletter preferences

Related Apps

MyFamilyTracker

Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.

Building something? Available for Android dev and QA consulting.

Work with me

Comments — powered by Giscus