Mobile Development 12 min read

Gradle Build Optimization for Android Projects: Analysis, Debugging, and Configuration

The article demonstrates a systematic approach to speeding up large Android Gradle builds by inspecting logs, stack traces, debugging plugins, profiling task execution with a custom plugin, and applying on‑demand configuration, caching, incremental and parallel compilation settings, which together cut build time by roughly 40 %.

37 Interactive Technology Team
37 Interactive Technology Team
37 Interactive Technology Team
Gradle Build Optimization for Android Projects: Analysis, Debugging, and Configuration

Background – Large Android SDK projects suffer from slow compilation and occasional build hangs. Improving the IDE (IntelliJ IDEA) and Gradle configuration is essential to speed up development.

Compilation analysis – The author outlines a four‑step analysis: log inspection, stack trace examination, plugin debugging, and task‑time profiling.

Log analysis – Running ./gradlew :androidx:assembleAt37GamesDebug -d reveals eight concurrent tasks, many of which wait for locks. The tasks include prepareGitHookConfig , packageDebugRenderscript , compileDebugAidl , processResources , and compileJava . The build lifecycle (Initialization → Configuration → Execution) is visualized, and the author adds listeners in settings.gradle to print the current phase.

Stack trace inspection – Using jps to find the JVM PID and jcmd <pid> Thread.print shows a thread stuck in java.net.PlainDatagramSocketImpl.receive0 holding several native locks. The thread is identified as a file‑lock request listener, indicating a Gradle internal lock contention rather than a problem in the project code.

Plugin debugging – The article demonstrates visual debugging (setting a breakpoint on a Gradle task) and custom plugin debugging by creating a Remote JVM Debug configuration, attaching it to the Gradle process, and running ./gradlew assembleDebug -Dorg.gradle.debug=true --no-daemon . Screenshots illustrate the steps.

Task‑time analysis – A custom Gradle plugin BuildTimeStatisticPlugin records start and end timestamps for each task and prints a sorted list of tasks by execution time after the build finishes. The relevant source code is:

class BuildTimeStatisticPlugin : AbstractPlugin() {
    // task execution map
    val taskRunTimeMap: MutableMap
by lazy { HashMap() }
    private var buildTimeSwitch: Boolean = true
    override fun applyPlugin(target: Project) {
        buildTimeSwitch = rootProject.properties["BUILD_TASK_TIME"] == "true"
        if (!buildTimeSwitch) return
        saveTaskExecuteTime(target)
        outputTaskExecuteTime(target)
    }
    private fun outputTaskExecuteTime(project: Project) {
        project.gradle.buildFinished {
            println("#########################################")
            println("build finish, print all task execute time")
            val sortList = ArrayList(taskRunTimeMap.values)
            sortList.sortByDescending { it.totalTime }
            sortList.forEach { task ->
                if (task.totalTime > 0) {
                    println("${task.path}  [${task.totalTime} ms]")
                }
            }
            println("#########################################")
        }
    }
    private fun saveTaskExecuteTime(project: Project) {
        project.gradle.addListener(object : TaskExecutionListener {
            override fun beforeExecute(task: Task) {
                val taskExecTimeInfo = TaskRunTimeEntity().apply {
                    startTime = System.currentTimeMillis()
                    path = task.path
                }
                taskRunTimeMap[task.path] = taskExecTimeInfo
            }
            override fun afterExecute(task: Task, state: TaskState) {
                taskRunTimeMap[task.path]?.let {
                    it.endTime = System.currentTimeMillis()
                    it.totalTime = it.endTime - it.startTime
                }
            }
        })
    }
}

class TaskRunTimeEntity {
    var totalTime: Long = 0
    var path: String? = null
    var startTime: Long = 0
    var endTime: Long = 0
}

The plugin prints the longest‑running tasks, helping to pinpoint bottlenecks without extra commands.

Optimization configuration – Several Gradle settings are applied:

On‑demand configuration: org.gradle.configureondemand=true

Build cache: org.gradle.caching=true and android.enableBuildCache=true

Kotlin incremental and parallel compilation: kotlin.incremental=true , kotlin.parallel.tasks.in.project=true

Kapt optimizations: kapt.use.worker.api=true , kapt.incremental.apt=true , kapt.include.compile.classpath=false

Configuration cache (experimental): org.gradle.unsafe.configuration-cache=true

These settings reduce redundant work, reuse previous outputs, and enable parallelism.

Results – After applying the optimizations, the build time for the demo module decreased by about 41 % (average of 10 runs after discarding the highest and lowest values). The build no longer hangs, and each module can be compiled independently into an AAR.

Conclusion – Gradle build performance depends on hardware, project size, dependency graph, and script complexity. Systematic analysis (logs, stack traces, task profiling) combined with targeted Gradle settings can substantially improve build speed for Android projects.

PerformanceAndroidbuild optimizationcachingGradleKotlinPlugin Debugging
37 Interactive Technology Team
Written by

37 Interactive Technology Team

37 Interactive Technology Center

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.