Use when setting up or refactoring Android Gradle build logic — convention plugins, composite builds, version catalogs, and shared build configuration across modules.
Centralise build logic in reusable Convention Plugins instead of copy-pasting build.gradle.kts configuration across modules.
root/
├── build-logic/
│ ├── convention/
│ │ ├── src/main/kotlin/
│ │ │ ├── AndroidApplicationConventionPlugin.kt
│ │ │ ├── AndroidLibraryConventionPlugin.kt
│ │ │ └── AndroidComposeConventionPlugin.kt
│ │ └── build.gradle.kts
│ └── settings.gradle.kts
├── gradle/
│ └── libs.versions.toml
├── app/
│ └── build.gradle.kts ← just: plugins { alias(libs.plugins.myapp.android.application) }
├── feature/home/
│ └── build.gradle.kts ← just: plugins { alias(libs.plugins.myapp.android.library) }
└── settings.gradle.kts
build-logic as a Composite Build// settings.gradle.kts
pluginManagement {
includeBuild("build-logic")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
build-logic/settings.gradle.kts// build-logic/settings.gradle.kts
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
}
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
rootProject.name = "build-logic"
include(":convention")
build-logic/convention/build.gradle.ktsplugins {
`kotlin-dsl`
}
dependencies {
compileOnly(libs.android.gradlePlugin)
compileOnly(libs.kotlin.gradlePlugin)
compileOnly(libs.ksp.gradlePlugin)
}
gradlePlugin {
plugins {
register("androidApplication") {
id = "myapp.android.application"
implementationClass = "AndroidApplicationConventionPlugin"
}
register("androidLibrary") {
id = "myapp.android.library"
implementationClass = "AndroidLibraryConventionPlugin"
}
register("androidCompose") {
id = "myapp.android.compose"
implementationClass = "AndroidComposeConventionPlugin"
}
}
}
// AndroidApplicationConventionPlugin.kt
import com.android.build.api.dsl.ApplicationExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
class AndroidApplicationConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
pluginManager.apply("com.android.application")
pluginManager.apply("org.jetbrains.kotlin.android")
extensions.configure<ApplicationExtension> {
compileSdk = 35
defaultConfig {
minSdk = 26
targetSdk = 35
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
}
}
}
}
// AndroidLibraryConventionPlugin.kt
import com.android.build.api.dsl.LibraryExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
class AndroidLibraryConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
pluginManager.apply("com.android.library")
pluginManager.apply("org.jetbrains.kotlin.android")
extensions.configure<LibraryExtension> {
compileSdk = 35
defaultConfig.minSdk = 26
compileOptions {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
}
}
}
}
// AndroidComposeConventionPlugin.kt
import com.android.build.api.dsl.CommonExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.getByType
class AndroidComposeConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
pluginManager.apply("org.jetbrains.kotlin.plugin.compose")
val extension = extensions.getByType<CommonExtension<*, *, *, *, *, *>>()
extension.buildFeatures.compose = true
}
}
}
# gradle/libs.versions.toml
[plugins]
myapp-android-application = { id = "myapp.android.application", version = "unspecified" }
myapp-android-library = { id = "myapp.android.library", version = "unspecified" }
myapp-android-compose = { id = "myapp.android.compose", version = "unspecified" }
// app/build.gradle.kts
plugins {
alias(libs.plugins.myapp.android.application)
alias(libs.plugins.myapp.android.compose)
alias(libs.plugins.hilt)
alias(libs.plugins.ksp)
}
android {
namespace = "com.example.app"
defaultConfig.applicationId = "com.example.app"
defaultConfig.versionCode = 1
defaultConfig.versionName = "1.0"
}
dependencies {
implementation(projects.feature.home)
implementation(libs.hilt.android)
ksp(libs.hilt.compiler)
}
// feature/home/build.gradle.kts
plugins {
alias(libs.plugins.myapp.android.library)
alias(libs.plugins.myapp.android.compose)
}
android.namespace = "com.example.feature.home"
The feature module's build file is now just 5 lines. All common configuration lives in the convention plugins.
build-logic included as a composite build in root settings.gradle.ktscompileSdk, minSdk, Java toolchain defined once in plugins — not in each modulebuild-logic itself resolves dependencies from the root version catalog