skip to content

When wiring androidTarget() in a KMP module, what AGP setup and source-set/configuration details matter for producing a usable AAR (compileSdk, namespace, publishLibraryVariants, dependency configurations, manifest)?

level: seniorimportance: nice to knowfreq 30%

answer

  1. androidTarget() needs com.android.library + android {} block
  2. namespace + compileSdk + minSdk required
  3. androidMain holds Android actuals; commonMain shared
  4. publishLibraryVariants(...) picks published AAR variants
  5. androidUnitTest / androidInstrumentedTest test source sets

basics

~20 s

You apply the Android library Gradle plugin, set compileSdk/minSdk and a namespace in the android {} block, and put Android-only code in androidMain. androidTarget() then builds an AAR that an Android app can depend on. You can pick which build variants get published.

solid answer

~40 s

androidTarget() requires the Android Gradle Plugin with com.android.library applied and an android { } block carrying namespace, compileSdk, and defaultConfig.minSdk. Shared code lives in commonMain; Android-specific actuals and Android-API usage go in androidMain. KMP dependency configurations map to Android ones via the source set (androidMain.dependencies { implementation(...) }). To publish, androidTarget { publishLibraryVariants("release", "debug") } controls which variants the AAR exposes. The Android library still needs a manifest (src/androidMain/AndroidManifest.xml), though manifest merging means a minimal one suffices and the package attribute is now replaced by namespace. The produced AAR bundles compiled classes plus resources/manifest and is consumed like any Gradle library. Modern KMP test source sets are androidUnitTest and androidInstrumentedTest.

code

kotlin · 21 lines
kotlin
plugins {
    kotlin("multiplatform")
    id("com.android.library")
}

kotlin {
    androidTarget {
        publishLibraryVariants("release", "debug")
    }
    sourceSets {
        androidMain.dependencies {
            implementation("androidx.core:core-ktx:1.13.0")
        }
    }
}

android {
    namespace = "com.example.shared"
    compileSdk = 35
    defaultConfig { minSdk = 24 }
}

go deeper

for a junior

Knows androidMain holds Android code and the module produces an AAR.

for a middle

Sets up the android {} block (namespace/compileSdk/minSdk) and per-source-set dependencies.

for a senior

Explains publishLibraryVariants, manifest/namespace handling, test source-set names, and how KMP configs map to AGP.

for a principal

Standardizes the AAR publishing/variant strategy across modules and reasons about consumer compatibility and minSdk policy.

## Plugin and android {} block `androidTarget()` only works if the **Android Gradle Plugin** is applied. For a shared library you use the **library** plugin: ```kotlin plugins { kotlin("multiplatform") id("com.android.library") } kotlin { androidTarget { publishLibraryVariants("release") // which variants the AAR publishes } iosArm64(); iosSimulatorArm64() } android { namespace = "com.example.shared" // required since AGP 8 compileSdk = 35 defaultConfig { minSdk = 24 } } ``` - **namespace** - required by modern AGP; defines the R class / package. - **compileSdk** - the Android SDK API level you compile against. - **minSdk** - lowest device API level supported. ## Source sets - `commonMain` - shared, platform-agnostic code; `expect` declarations. - `androidMain` - Android-specific `actual` implementations and any code touching the Android SDK (e.g., `android.content.Context`). - Test source sets: `androidUnitTest` (local JVM unit tests) and `androidInstrumentedTest` (on-device/instrumented). These names supersede older `androidTest`/`androidAndroidTest` confusion fixed in newer KMP. Dependencies are declared per source set and map onto Android configurations: ```kotlin sourceSets { androidMain.dependencies { implementation("androidx.core:core-ktx:1.13.0") } } ``` ## Manifest An Android library AAR carries a manifest. Provide `src/androidMain/AndroidManifest.xml`; with AGP 8+ the `package` attribute is replaced by `namespace`, so the manifest can be minimal (`<manifest />`). It merges into consumers via manifest merging. ## Publishing variants - `publishLibraryVariants("release", "debug")` - choose which build variants the published AAR includes; without it, defaults may publish only release or fail to disambiguate. - `publishLibraryVariantsGroupedByFlavor = true` - group multi-flavor outputs. - The resulting **AAR** (Android Archive) packages compiled bytecode + resources + manifest and is consumed by an app module as a normal `implementation(project(":shared"))` or from a Maven repo. ## Why this matters Getting `namespace`/`compileSdk` wrong stops the AAR from building; wrong source-set names silently drop your Android `actual`s; missing `publishLibraryVariants` causes ambiguous-variant publish errors. The Android side is 'just' a Kotlin/JVM target wrapped by AGP, but the AGP wiring is the part teams get wrong.

  • What goes in androidMain versus commonMain?
    commonMain holds shared, platform-agnostic code and expect declarations; androidMain holds the Android actual implementations and any code that touches the Android SDK (Context, etc.).
  • What does publishLibraryVariants control?
    Which Android build variants (release/debug/flavors) the published AAR exposes, preventing ambiguous-variant publish errors.

saying these in an interview costs you the question

  • Forgets the Android Gradle Plugin must be applied for androidTarget()
  • Omits namespace/compileSdk and assumes it builds
  • Puts Android-SDK code in commonMain
  • Doesn't know how dependencies map to Android source-set configurations

context