When to Use
- Build times are slow (clean or incremental)
- Investigating build performance regressions
- Analyzing Gradle Build Scans
- Identifying configuration vs execution bottlenecks
- Optimizing CI/CD build times
- Enabling Gradle Configuration Cache
- Reducing unnecessary recompilation
- Debugging kapt/KSP annotation processing
Example Prompts
- "My builds are slow, how can I speed them up?"
- "How do I analyze a Gradle build scan?"
- "Why is configuration taking so long?"
- "Why does my project always recompile everything?"
- "How do I enable configuration cache?"
- "Why is kapt so slow?"
Workflow
- Measure Baseline — Clean build + incremental build times
- Generate Build Scan —
./gradlew assembleDebug --scan
- Identify Phase — Configuration? Execution? Dependency resolution?
- Apply ONE optimization — Don't batch changes
- Measure Improvement — Compare against baseline
- Verify in Build Scan — Visual confirmation
Quick Diagnostics
Generate Build Scan
bash
1./gradlew assembleDebug --scan
Profile Build Locally
bash
1./gradlew assembleDebug --profile
2# Opens report in build/reports/profile/
Build Timing Summary
bash
1./gradlew assembleDebug --info | grep -E "^\:.*"
2# Or view in Android Studio: Build > Analyze APK Build
Build Phases
| Phase | What Happens | Common Issues |
|---|
| Initialization | settings.gradle.kts evaluated | Too many include() statements |
| Configuration | All build.gradle.kts files evaluated | Expensive plugins, eager task creation |
| Execution | Tasks run based on inputs/outputs | Cache misses, non-incremental tasks |
Identify the Bottleneck
Build scan → Performance → Build timeline
- Long configuration phase: Focus on plugin and buildscript optimization
- Long execution phase: Focus on task caching and parallelization
- Dependency resolution slow: Focus on repository configuration
12 Optimization Patterns
1. Enable Configuration Cache
Caches configuration phase across builds (AGP 8.0+):
properties
1# gradle.properties
2org.gradle.configuration-cache=true
3org.gradle.configuration-cache.problems=warn
2. Enable Build Cache
Reuses task outputs across builds and machines:
properties
1# gradle.properties
2org.gradle.caching=true
3. Enable Parallel Execution
Build independent modules simultaneously:
properties
1# gradle.properties
2org.gradle.parallel=true
4. Increase JVM Heap
Allocate more memory for large projects:
properties
1# gradle.properties
2org.gradle.jvmargs=-Xmx4g -XX:+UseParallelGC
5. Use Non-Transitive R Classes
Reduces R class size and compilation (AGP 8.0+ default):
properties
1# gradle.properties
2android.nonTransitiveRClass=true
6. Migrate kapt to KSP
KSP is 2x faster than kapt for Kotlin:
kotlin
1// Before (slow)
2kapt("com.google.dagger:hilt-compiler:2.51.1")
3
4// After (fast)
5ksp("com.google.dagger:hilt-compiler:2.51.1")
7. Avoid Dynamic Dependencies
Pin dependency versions:
kotlin
1// BAD: Forces resolution every build
2implementation("com.example:lib:+")
3implementation("com.example:lib:1.0.+")
4
5// GOOD: Fixed version
6implementation("com.example:lib:1.2.3")
8. Optimize Repository Order
Put most-used repositories first:
kotlin
1// settings.gradle.kts
2dependencyResolutionManagement {
3 repositories {
4 google() // First: Android dependencies
5 mavenCentral() // Second: Most libraries
6 // Third-party repos last
7 }
8}
9. Use includeBuild for Local Modules
Composite builds are faster than project() for large monorepos:
kotlin
1// settings.gradle.kts
2includeBuild("shared-library") {
3 dependencySubstitution {
4 substitute(module("com.example:shared")).using(project(":"))
5 }
6}
10. Enable Incremental Annotation Processing
properties
1# gradle.properties
2kapt.incremental.apt=true
3kapt.use.worker.api=true
11. Avoid Configuration-Time I/O
Don't read files or make network calls during configuration:
kotlin
1// BAD: Runs during configuration
2val version = file("version.txt").readText()
3
4// GOOD: Defer to execution
5val version = providers.fileContents(file("version.txt")).asText
12. Use Lazy Task Configuration
Avoid create(), use register():
kotlin
1// BAD: Eagerly configured
2tasks.create("myTask") { ... }
3
4// GOOD: Lazily configured
5tasks.register("myTask") { ... }
Common Bottleneck Analysis
Slow Configuration Phase
Symptoms: Build scan shows long "Configuring build" time
Causes & Fixes:
| Cause | Fix |
|---|
| Eager task creation | Use tasks.register() instead of tasks.create() |
| buildSrc with many dependencies | Migrate to Convention Plugins with includeBuild |
| File I/O in build scripts | Use providers.fileContents() |
| Network calls in plugins | Cache results or use offline mode |
Slow Compilation
Symptoms: :app:compileDebugKotlin takes too long
Causes & Fixes:
| Cause | Fix |
|---|
| Non-incremental changes | Avoid build.gradle.kts changes that invalidate cache |
| Large modules | Break into smaller feature modules |
| Excessive kapt usage | Migrate to KSP |
| Kotlin compiler memory | Increase kotlin.daemon.jvmargs |
Cache Misses
Symptoms: Tasks always rerun despite no changes
Causes & Fixes:
| Cause | Fix |
|---|
| Unstable task inputs | Use @PathSensitive, @NormalizeLineEndings |
| Absolute paths in outputs | Use relative paths |
Missing @CacheableTask | Add annotation to custom tasks |
| Different JDK versions | Standardize JDK across environments |
CI/CD Optimizations
Remote Build Cache
kotlin
1// settings.gradle.kts
2buildCache {
3 local { isEnabled = true }
4 remote<HttpBuildCache> {
5 url = uri("https://cache.example.com/")
6 isPush = System.getenv("CI") == "true"
7 credentials {
8 username = System.getenv("CACHE_USER")
9 password = System.getenv("CACHE_PASS")
10 }
11 }
12}
Gradle Enterprise / Develocity
For advanced build analytics:
kotlin
1// settings.gradle.kts
2plugins {
3 id("com.gradle.develocity") version "3.17"
4}
5
6develocity {
7 buildScan {
8 termsOfUseUrl.set("https://gradle.com/help/legal-terms-of-use")
9 termsOfUseAgree.set("yes")
10 publishing.onlyIf { System.getenv("CI") != null }
11 }
12}
Skip Unnecessary Tasks in CI
bash
1# Skip tests for UI-only changes
2./gradlew assembleDebug -x test -x lint
3
4# Only run affected module tests
5./gradlew :feature:login:test
Android Studio Settings
File → Settings → Build → Gradle
- Gradle JDK: Match your project's JDK
- Build and run using: Gradle (not IntelliJ)
- Run tests using: Gradle
File → Settings → Build → Compiler
- Compile independent modules in parallel: ✅ Enabled
- Configure on demand: ❌ Disabled (deprecated)
Verification Checklist
After optimizations, verify:
References