build.gradle is the main build file of an Android project on Gradle that contains instructions for compiling, packaging and signing the application. Each module in the project has its own build.gradle: one at the project level (project-level) and one for each module (module-level). According to Google Android Developers, 2025, proper build.gradle configuration speeds up builds by up to 40% and eliminates dependency conflicts. The syntax supports two languages: Groovy (build.gradle) and Kotlin DSL (build.gradle.kts).
Key Takeaways
build.gradle is a build script written in Groovy (.gradle extension) or Kotlin (.gradle.kts) that manages all aspects of Android application compilation. Gradle is an automated build system adopted by Google in 2013 as the standard for Android. build.gradle describes: which plugins are applied (Android, Kotlin, libraries), which dependencies are connected, which SDK versions are used, how to sign the application and where to publish.
The build process includes three phases: Initialization (module discovery), Configuration (executing build.gradle scripts), Execution (executing tasks). build.gradle runs during the Configuration phase, when Gradle creates the task graph. At this point Build Variants are determined, dependencies are computed and tasks are configured. Important: build.gradle is code, not just configuration. It can use conditions, loops, method calls and external scripts.
Gradle files are stored in the module root (app/build.gradle) and the project root (build.gradle). Additionally, Gradle supports apply from — including external Gradle scripts. This allows extracting repetitive logic into files with shared settings. With the advent of Convention Plugins (AGP 7+), apply from is considered deprecated — Convention Plugins provide a type-safe and composable way to reuse configuration across modules.
Since 2013, build.gradle syntax has undergone significant changes: from Groovy with dynamic configurations to Kotlin DSL with compile-time checks. AGP has evolved from version 1.0 to 8.7 (2025). Key milestones: AGP 3.0 (Java 8 desugar, new variant API), AGP 4.0 (view binding, Java 11), AGP 7.0 (Kotlin DSL by default, Java 11 min), AGP 8.0 (non-transitive R classes, build config in Kotlin), AGP 8.7 (KSP instead of kapt, fast configuration).
Project-level build.gradle (root) defines plugins, repositories and configurations common to all modules. Main blocks: plugins (Gradle plugin declarations), repositories (dependency sources: mavenCentral, google, jitpack). The root build.gradle usually does not have an android block — it appears in modules. Project-level can also contain a subprojects block for common configuration of all subprojects, although Convention Plugins are preferred.
Module-level build.gradle (e.g. app/build.gradle) describes a specific module. If the module is an application, it applies the com.android.application plugin. If a library — com.android.library. Module-level contains: android block (compileSdk, defaultConfig, buildTypes, productFlavors), dependencies block (module dependencies) and optionally blocks for test configuration and packaging. Module-level runs after project-level and can override common settings.
Starting from AGP 8.0, the root build.gradle can use version catalogs (libs.versions.toml) for centralized dependency version management. A version catalog is a file in the gradle/ directory that contains versions, libraries and plugins. In build.gradle dependencies are connected via libs: implementation(libs.retrofit). Version catalogs are mandatory for new projects and recommended for all projects with three or more modules.
// settings.gradle.kts — project root
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
// build.gradle.kts (project-level)
plugins {
id("com.android.application") version "8.7.0" apply false
id("org.jetbrains.kotlin.android") version "2.0.21" apply false
}
// app/build.gradle.kts (module-level)
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("com.google.devtools.ksp")
}
android {
namespace = "com.example.myapp"
compileSdk = 35
defaultConfig {
applicationId = "com.example.myapp"
minSdk = 26
targetSdk = 35
versionCode = 1
versionName = "1.0.0"
}
}
Groovy is a dynamic JVM language that was the original Gradle syntax. Groovy scripts (.gradle) use dynamic typing: you can omit types, use quoted or unquoted strings, call methods that don't exist at compile time. Groovy's flexibility is also its drawback: the IDE cannot verify syntax and types until the script is executed, leading to runtime errors with incorrect parameter names or types.
Kotlin DSL (.gradle.kts) uses Kotlin's static typing. The IDE checks types, suggests available parameters through autocomplete and highlights errors at edit time. Kotlin DSL is slower during the Configuration phase (due to compiling .kts files to bytecode), but Google continuously improves performance: AGP 8.5+ uses Gradle Configuration Cache and Caching Kotlin DSL compilation, reducing the difference to 1-2 seconds.
Google recommends Kotlin DSL for all new projects and gradual migration of existing ones. Migration from Groovy to Kotlin DSL is straightforward: quotes are replaced with parentheses, types are added, operators are converted to functions. Most libraries provide Kotlin DSL examples in their documentation. For complex cases (Custom Plugin, Task Graph), Kotlin DSL provides a type-safe API and prevents errors that in Groovy are only discovered at runtime. Version catalogs (libs.versions.toml) work identically with both syntaxes.
| Feature | Groovy (.gradle) | Kotlin DSL (.gradle.kts) |
|---|---|---|
| Typing | Dynamic | Static |
| IDE support | Limited | Full (autocomplete, types) |
| Configuration speed | Faster (no compilation) | Slower (.kts compilation) |
| Errors | Runtime | Compile-time |
| Recommendation | Legacy projects only | New projects and migration |
Block android is the central element of module-level build.gradle. Inside it are configured: namespace (for R and BuildConfig), compileSdk, defaultConfig, buildTypes, productFlavors, sourceSets, compileOptions, packaging, bundle. All parameters of the android block apply only to Android modules. If the module is a library, the library plugin is used instead of application, and applicationId is absent from the android block.
compileSdk is the SDK version used to compile the code. It should be the latest Android API (at the time of writing — 35). minSdk is the minimum API version for support. targetSdk is the version the app targets (behavioral changes of this version apply). The difference between compileSdk and targetSdk: compileSdk determines available APIs, targetSdk determines runtime behavior. Recommendation: compileSdk = latest, targetSdk = latest - 1 (for testing adaptation to new changes).
compileOptions sets Java compatibility: sourceCompatibility and targetCompatibility. AGP 8+ requires Java 17+ for compilation. packaging manages file inclusion from libraries: exclude, merge, pickFirst for resolving META-INF conflicts. buildFeatures enables/disables ViewBinding, DataBinding, Compose. aaptOptions configures resource processing: ignoreAssetsPattern, cruncherEnabled. Each element of the android block optimizes a specific aspect of the build.
android {
namespace = "com.example.myapp"
compileSdk = 35
buildToolsVersion = "35.0.0"
defaultConfig {
applicationId = "com.example.myapp"
minSdk = 26
targetSdk = 35
versionCode = 5
versionName = "2.3.1"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
getByName("debug") { isDebuggable = true }
getByName("release") {
isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
buildFeatures {
viewBinding = true
compose = true
}
}
Dependencies in build.gradle are libraries and modules connected to the project. The dependencies block sits at the same level as the android block. Gradle supports several configurations: implementation (library is available in this module, not transitive), api (library is transitively available to dependent modules), compileOnly (compile-time only, not included in APK), runtimeOnly (runtime only), annotationProcessor / ksp (annotation processors), testImplementation (test only), androidTestImplementation (instrumentation tests only).
Starting from AGP 8.0, Non-Transitive R classes — each library has its own R class, preventing resource conflicts. In the dependencies block it is important to use the correct configurations: implementation does not expose transitive dependencies, speeding up builds. api exposes them — used when a library exports types from another library (e.g. Retrofit uses OkHttp types in its public API).
For version management it is recommended to use BOM (Bill of Materials) — a build file that defines compatible library versions. Firebase BOM: implementation(platform("com.google.firebase:firebase-bom:33.0.0")). After connecting BOM, you can specify only the library name without version — BOM will automatically select a compatible version. This eliminates conflicts between transitive dependencies of different libraries. BOMs are available for Firebase, Compose, Kotlin, Ktor, AndroidX.
dependencies {
// BOM — version management
implementation(platform("androidx.compose:compose-bom:2024.12.01"))
implementation(platform("com.google.firebase:firebase-bom:33.0.0"))
// AndroidX and Compose
implementation("androidx.core:core-ktx")
implementation("androidx.lifecycle:lifecycle-runtime-ktx")
implementation("androidx.activity:activity-compose")
implementation("androidx.compose.ui:ui")
// Network
implementation("com.squareup.retrofit2:retrofit:2.11.0")
implementation("com.squareup.okhttp3:okhttp:4.12.0")
// Firebase (versions from BOM)
implementation("com.google.firebase:firebase-firestore")
implementation("com.google.firebase:firebase-crashlytics")
// Testing
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.2.1")
}
In multi-module projects, each module has its own build.gradle. To connect one module to another, use the syntax implementation(project(":module-name")). Gradle automatically rebuilds the module if its configuration has changed. Multi-module architecture improves build time (incremental builds, parallelism) and separates responsibilities between feature modules, core modules and libraries.
The key problem of multi-module projects is configuration duplication. If 10 modules have the same minSdk, compileSdk and Compose dependencies, that is 10 copies in different build.gradle files. The solution is Convention Plugins (previously buildSrc). A Convention Plugin is a Gradle plugin written in Kotlin that is applied to modules: plugins { id("myapp.android.library") }. The plugin contains common configuration, and changes apply immediately to all modules.
To organize Convention Plugins, the build-logic/ directory is used in the project root. It contains includeBuild in settings.gradle and Kotlin plugins. Convention Plugins can be published to a maven repository for reuse across projects. Google recommends Convention Plugins as the standard for multi-module projects, replacing subprojects { } and apply from. Switching to Convention Plugins reduces the module's build.gradle to 10-15 lines.
// build-logic/src/main/kotlin/AndroidLibraryConventionPlugin.kt
class AndroidLibraryConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
with(plugins) {
apply("com.android.library")
apply("org.jetbrains.kotlin.android")
}
extensions.configure<CommonExtension<*, *, *, *>> {
compileSdk = 35
defaultConfig { minSdk = 26 }
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
}
}
}
}
// module/build.gradle.kts — after Convention Plugin
plugins {
id("myapp.android.library")
}
dependencies {
implementation(project(":core:network"))
}
Frequently Asked Questions
Kotlin DSL (.gradle.kts) is Google's official recommendation. Static typing prevents errors, the IDE provides autocomplete. Groovy (.gradle) is supported, but new Gradle and AGP features are tested primarily on Kotlin DSL.
namespace defines the package for generated classes (R.java, BuildConfig). Previously namespace was set in AndroidManifest.xml. Starting with AGP 7+, namespace is specified only in build.gradle. The value must match applicationId (or differ if applicationIdSuffix is used).
Enable Gradle Configuration Cache (org.gradle.configuration-cache=true), use Build Cache (org.gradle.caching=true), switch to KSP instead of kapt, split the multi-module project and use Convention Plugins. Also disable unnecessary product flavors: for debug builds only one flavor.
implementation: the dependency is visible only inside the module. Dependent modules do not get access to transitive classes. api: the dependency is exposed externally. Use api when types from the dependency are used in the module's public API (for example, Retrofit exports OkHttp types). implementation speeds up builds — Gradle does not rebuild dependent modules when an implementation dependency changes.
build.gradle is an Android-specific file. iOS uses Xcode project (.xcodeproj) and Swift Package Manager (Package.swift). However, there are cross-platform tools (Kotlin Multiplatform, Flutter, React Native) where build.gradle is used to build the Android part. In KMP, build.gradle configures the Android target.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also