Gradle Basics
Gradle Basics
Gradle is the dominant build tool in the Android ecosystem and a serious competitor to Maven in the JVM world. It replaces XML with a real programming language — either Groovy DSL or Kotlin DSL — giving you the full expressive power of a scripting environment while still offering the declarative model you need for predictable builds. This lesson covers the build.gradle file structure, the task model, and the Groovy/Kotlin DSL in depth.
Why Gradle?
Maven's XML is verbose and rigid. Gradle keeps the best parts — declarative dependency management, a rich plugin ecosystem, convention over configuration — and removes the worst:
- Incremental builds. Gradle tracks inputs and outputs of every task. If nothing changed, the task is marked UP-TO-DATE and skipped. Maven re-runs phases even when nothing has changed.
- Build cache. Task outputs are stored and reused across machines, dramatically cutting CI build times for large projects.
- Flexible scripting. The build file is a real program; you can use loops, conditionals, and helper functions.
- Performance-first daemon. The Gradle daemon runs in the background so subsequent builds skip JVM startup entirely.
build.gradle.kts). It gives you IDE auto-complete, refactoring support, and compile-time type checking. The Groovy DSL (build.gradle) is older and still very common, so you will encounter both — this lesson shows both syntaxes side-by-side where they differ.
The build.gradle Structure
A minimal Java project build file has four sections: plugins, repositories, dependencies, and (optionally) task configuration.
Groovy DSL (build.gradle):
Kotlin DSL (build.gradle.kts) — the same project:
The Kotlin DSL wraps everything in function calls (parentheses required), uses double-quoted strings, and resolves types at compile time — errors in your build script are caught before the build even runs.
Dependency Configurations
Gradle's Java plugin ships with several configurations. The most important ones:
implementation— on the compile classpath of this project; NOT leaked to consumers. Prefer this.api— (from thejava-libraryplugin) leaked to consumers; use only for types your public API exposes.compileOnly— only available at compile time (e.g., Lombok, annotation processors).runtimeOnly— only on the runtime classpath (e.g., JDBC drivers).testImplementation— likeimplementation, scoped to tests.testRuntimeOnly— runtime-only for tests (e.g., JUnit launcher).
implementation over the deprecated compile. The old compile configuration leaked all transitive dependencies to consumers, inflating their classpaths. implementation keeps internal dependencies internal, which means faster compilation and cleaner dependency trees.
The Gradle Task Model
Everything Gradle does is a task. A task is a unit of work with typed inputs and outputs. When you run ./gradlew build, Gradle builds a directed acyclic graph (DAG) of all tasks that need to run and executes them in dependency order.
Common built-in tasks from the Java plugin:
compileJava— compilessrc/main/java.processResources— copiessrc/main/resourcesto the output directory.classes— lifecycle task: depends oncompileJava+processResources.test— compiles and runs tests.jar— packages compiled classes into a JAR.build— assembles and tests the project (the standard full build).clean— deletes thebuild/directory.
Defining Custom Tasks
You can define your own tasks directly in the build file. The Groovy DSL makes this particularly concise:
Kotlin DSL equivalent:
Typed Tasks — the Preferred Style
For serious tasks, Gradle provides typed base classes. Using a typed task lets Gradle track inputs and outputs for incremental execution:
Because from and into are declared as task inputs/outputs, Gradle will skip this task automatically when neither the Javadoc source nor the destination has changed.
doFirst/doLast to configure a task's inputs or outputs. Configuration code that runs at configuration time belongs in the task's configuration block; action code (actual work) belongs in doLast. Mixing them breaks the incremental build and the build cache.
The Gradle Wrapper
Every project should commit the Gradle wrapper — a small shell script and JAR that downloads the correct Gradle version automatically. This ensures every developer and every CI server uses exactly the same Gradle release:
The wrapper configuration is stored in gradle/wrapper/gradle-wrapper.properties. Commit this file and the wrapper scripts; do NOT commit the .gradle/ cache directory.
Essential CLI Commands
./gradlew tasks— list all available tasks grouped by category../gradlew build— compile, test, and package../gradlew test— run tests only../gradlew dependencies— print the full dependency tree../gradlew :subproject:build— build a specific subproject../gradlew build --scan— generate a build scan at scans.gradle.com (invaluable for diagnosing slow builds).
Summary
Gradle replaces XML with a real DSL (Groovy or Kotlin), models every action as a task with explicit inputs and outputs, and uses incremental execution to only rebuild what changed. The wrapper guarantees build reproducibility across every environment. In the next lesson we will go deeper into dependency management, version catalogs, and writing custom Gradle tasks that integrate with multi-project builds.