plugins/android-skills/skills/android-gradle-logic/SKILL.md
Use when setting up or refactoring Android Gradle build logic — convention plugins, composite builds, version catalogs, and shared build configuration across modules.
npx skillsauth add rcosteira79/android-skills android-gradle-logicInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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
}
}
extensions.configure<org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension> {
jvmToolchain(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
}
extensions.configure<org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension> {
jvmToolchain(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.
The convention plugin examples above target AGP 8. AGP 9 changes several things that hit build logic directly: it drops the standalone org.jetbrains.kotlin.android plugin (Kotlin is built into com.android.application / com.android.library), removes BaseExtension and the old variant APIs (applicationVariants → androidComponents { onVariants { … } }), moves kotlinOptions {} to a top-level kotlin { compilerOptions { … } }, and makes kapt incompatible (migrate to KSP). Any convention plugin that touches these needs updating.
Defer to the dedicated migration skills for the mechanics rather than duplicating the steps here: Google's agp-9-upgrade for pure-Android projects, JetBrains' kotlin-tooling-agp9-migration for KMP, and this repo's gradle-build-performance skill for the kapt → KSP step.
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 catalogdevelopment
Use when writing, fixing, or refactoring Android/KMP code in Kotlin — Android's three-tier test model, fake-first strategy, coroutine testing, and Compose UI testing, on a test-first (RED-GREEN-REFACTOR) foundation.
development
Use when debugging Android or KMP issues — Android-specific techniques covering Logcat, ADB, ANR traces, R8 stack trace decoding, memory leaks, Gradle build failures, and Compose recomposition bugs, on a root-cause-first foundation.
testing
Use when implementing paginated lists in Android or Compose with Paging 3 — PagingSource, Pager and PagingConfig setup, RemoteMediator for offline-first lists, LazyPagingItems and itemKey integration in LazyColumn, dynamic filters via flatMapLatest, and unit tests with TestPager and asSnapshot. Triggers include Paging 3, infinite list, infinite scroll, paginated list, LazyPagingItems, collectAsLazyPagingItems, and cachedIn.
development
Use when setting up or working with Koin in Android or KMP projects — module declarations with Classic DSL or KSP annotations, ViewModel injection in Compose, scopes, Nav 3 entry providers, application startup, and compile-time verification via `verify()`. Triggers on Koin, `single`, `factory`, `koinViewModel`, `koinInject`, `parametersOf`, `startKoin`, "KMP DI", "shared DI".