Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

Dompting the Elephant: Making Sense of Gradle f...

Dompting the Elephant: Making Sense of Gradle for Android Developers

In this talk, we’ll demystify how Gradle works under the hood. You’ll gain a clear mental model of the build lifecycle, understand the difference between configuration and execution phases, and learn how tasks, plugins, and dependencies interact.

Avatar for Yves Kalume

Yves Kalume

November 06, 2025
Tweet

More Decks by Yves Kalume

Other Decks in Programming

Transcript

  1. Project public interface Project extends Comparable<Project> { String DEFAULT_BUILD_FILE =

    "build.gradle"; String DEFAULT_BUILD_DIR_NAME = "build"; String GRADLE_PROPERTIES = "gradle.properties"; File getProjectDir(); File getBuildDir(); void apply(Map<String, ?> options); void afterEvaluate(Action<? super Project> action); void dependencies(Action<? super DependencyHandler> action); void repositories(Action<? super RepositoryHandler> action); Task task(String name); ... }
  2. Initialization (Build phases) • Create a settings instance and evaluate

    settings.gradle(.kts) • Determine which project are included and create their instances
  3. Con fi guration (Build phases) • Reads each build.gradle •

    Con fi gures a Project instance for every module
  4. Con fi guration (Build phases) processTestResources Build assemble jar check

    classes compileJava processResources test testClasses compileTestJava DAG (Directed Acyclic Graph) dependsOn dependsOn
  5. Plugin android { compileSdk = 34 defaultConfig { applicationId =

    "ke.droidcon.app" } } plugins { id("com.android.application") id("org.jetbrains.kotlin.android") }
  6. Plugin abstract class SamplePlugin : Plugin<Project> { override fun apply(project:

    Project) { project.tasks.register("MyCustomTask") { doLast { println("Hello world from the build file!") } } } }
  7. Binary plugin MyProject ├── my-plugin │ ├── themodule │ │

    ├── src │ │ └── build.gradle.kts │ └── settings.gradle.kts ├── app │ └── src └── settings.gradle.kts
  8. Binary plugin plugins { `kotlin-dsl` } gradlePlugin { plugins {

    create("droidconke") { id = "ke.droidcon.greeting" implementationClass = "ke.droidcon.GreetingPlugin" } } } MyProject/my-plugin/build.gradle.kts
  9. abstract class GreetingTask : DefaultTask() { @Input var name =

    "Droidcon" @OutputFile val myFile: File = File(name) @TaskAction fun action() { println("Hello $name Karibu droidcon") myFile.createNewFile() myFile.writeText(fileText.get()) } } MyProject/my-plugin/src/main/kotlin/…
  10. abstract class SamplePlugin : Plugin<Project> { override fun apply(project: Project)

    { project.tasks.register<GreetingTask>("greet") { name = "Kalume" } } } MyProject/my-plugin/src/main/kotlin/…
  11. abstract class SamplePlugin : Plugin<Project> { override fun apply(project: Project)

    { project.tasks.register<GreetingTask>("greet") { name = "Kalume" } } } MyProject/my-plugin/src/main/kotlin/…
  12. abstract class SamplePlugin : Plugin<Project> { override fun apply(project: Project)

    { val extension = project.extensions.create<GreetingPluginExtension>("greetingConfig") project.tasks.register<GreetingTask>("greet") { name = extension.name.get() } } } MyProject/my-plugin/src/main/kotlin/…
  13. plugin { id("ke.droidcon.greeting") } greetingConfig { name = "Kalume" }

    ./gradlew greet MyProject/app/build.gradle.kts
  14. Graph resolution phase • Gradle looks at all the declared

    dependencies • Transitive dependencies • Constructs a resolved dependency graph
  15. Component A speci fi c version of a library kotlinx-serialization-json:1.7.1

    kotlinx-serialization-json:1.6.0 Metadata what to know about that component. a .pom or .ivy fi le component’s dependencies available variants Attributes key-value pairs that describe characteristics of a variant E.g : org.gradle.jvm.version=17 Variants Each variant is designed for a di ff erent target platform or usage scenario. E.g : JVM, Kotlin/Native, JS or the context it’s being used in
  16. How gradle build the Dependency Graph My App Library A

    1.3.2 Library C 1.0.2 Library B 1.2.3 Library C 2.0.4 My App Library A 1.3.2 Library B 1.2.3 Library C 2.0.4
  17. ./gradlew dependencies compileClasspath - Compile classpath for 'main'. + ---

    org.jetbrains.kotlin:kotlin-stdlib:2.2.10 | \ --- org.jetbrains:annotations:13.0 \ --- org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0 \ --- org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.9.0 + --- org.jetbrains.kotlinx:kotlinx-serialization-bom:1.9.0 | + --- org.jetbrains.kotlinx:kotlinx-serialization-core:1.9.0 (c) | + --- org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.9.0 (c) | + --- org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0 (c) | \ --- org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.9.0 (c) + --- org.jetbrains.kotlin:kotlin-stdlib:2.2.0 -> 2.2.10 (*) \ --- org.jetbrains.kotlinx:kotlinx-serialization-core:1.9.0 \ --- org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.9.0 + --- org.jetbrains.kotlinx:kotlinx-serialization-bom:1.9.0(*) \ --- org.jetbrains.kotlin:kotlin-stdlib:2.2.0 -> 2.2.10 (*)
  18. ./gradlew dependencies compileClasspath - Compile classpath for 'main'. + ---

    org.jetbrains.kotlin:kotlin-stdlib:2.2.10 | \ --- org.jetbrains:annotations:13.0 \ --- org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0 \ --- org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.9.0 + --- org.jetbrains.kotlinx:kotlinx-serialization-bom:1.9.0 | + --- org.jetbrains.kotlinx:kotlinx-serialization-core:1.9.0 (c) | + --- org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.9.0 (c) | + --- org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0 (c) | \ --- org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.9.0 (c) + --- org.jetbrains.kotlin:kotlin-stdlib:2.2.0 -> 2.2.10 (*) \ --- org.jetbrains.kotlinx:kotlinx-serialization-core:1.9.0 \ --- org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.9.0 + --- org.jetbrains.kotlinx:kotlinx-serialization-bom:1.9.0(*) \ --- org.jetbrains.kotlin:kotlin-stdlib:2.2.0 -> 2.2.10 (*)
  19. Artifact Resolution Gradle : “Alright, I know what I need;

    now let’s go and get it.” • Contacts the repositories you’ve declared dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() maven { url = uri("https: // yveskalume.dev") } } }
  20. Artifact Resolution Gradle : “Alright, I know what I need;

    now let’s go and get it.” • Downloads these fi les into its local cache • Contacts the repositories you’ve declared
  21. Deamon • A long-lived background process • Executes your builds

    much more quickly • Avoid the expensive bootstrapping process • Leverage caching, by keeping data about your project in memory.
  22. gradlew • Starts a tiny JVM using gradle/wrapper/gradle-wrapper.jar • That

    JVM locates or downloads Gradle from gradle/wrapper/gradle- wrapper.properties • Uses Java re fl ection to start Gradle itself inside the same process • That tiny JVM essentially becomes the Gradle Client JVM
  23. What Can Go Wrong with the Gradle Daemon • Memory

    leaks or corruption of global state • Builds that don’t release resources properly Solution • Kill the process manually • --no-daemon fl ag
  24. --no-daemon • The client JVM may convert itself into a

    daemon JVM (if it is compatible with build requirements) • The build happens inside the single JVM • If not compatible a new disposable JVM will still be started for the build and exit at the end of the build
  25. • It’s detecting more than one Java version in your

    environment. • Your Gradle JDK and your system JAVA_HOME point to di ff erent Java installations • Not dangerous, but it increases memory usage unnecessarily • Just make sure your Gradle JDK matches the JAVA_HOME “Multiple Gradle daemons might be spawned because the Gradle JDK and JAVA_HOME locations are di ff erent.”
  26. Remember about Tasks ? A task can actually be seen

    like a pure function. Action Input Output
  27. Remember about Tasks ? A task can actually be seen

    like a pure function. It can be can be deterministic Task 1 Input Output Task 2 Etc
  28. Gradle cache Local cache Remote cache In-memory (Deamon) in-project Out

    of the project Remote Build cache Proxy Con fi guration cache Build folder Dependency cache Build cache
  29. Build folder • Execution phase • Analyze input and compare

    with the most recent run • {project}/.gradle/version/executionHistory/ executionHistory.bin • Checks your module’s build/ folder and reuses those results • ./gradlew clean
  30. Dependency cache • ~/.gradle/caches/modules-2 • Stores jars, aars, and poms

    fi les • Using a proxy for Maven repositories can be a good idea
  31. Build cache • Reuse task outputs from any earlier build

    in any location on the local machine • ~/.gradle/caches/build-cache-1 • org.gradle.caching=true
  32. Build cache key Cache Key Source files -> Hash Compiler

    args -> Hash Java version -> Hash ~/.gradle/caches/build-cache-1
  33. Gradle caching Module build folder Task Input Task Task Action

    Dorg.gradle.caching.debug=true Cache Key Task Output Cached Cache misses
  34. Remote Build cache • Build cache can be saved remotely

    • It’s recommended to generate cache from the CI
  35. Conclusion • Stop thinking of Gradle as a black box

    • Treat your build script like real code • Don’t panic ! • Play with it