Set up the Android Gradle Library Plugin for KMP

The com.android.kotlin.multiplatform.library Gradle plugin is the officially supported tool for adding an Android target to a Kotlin Multiplatform (KMP) library module. It simplifies project configuration, improves build performance, and offers better integration with Android Studio.

The previous approach is now deprecated in favor of the plugin, also referred to as the Android-KMP plugin. Continuing to use the com.android.library plugin for KMP will no longer be supported by JetBrains and won't benefit from future updates and improvements.

To apply this plugin, refer to the Apply the Android-KMP plugin section. If you need to migrate from the legacy APIs, check the Migration Guide.

Key features and differences

The Android-KMP plugin is tailored specifically for KMP projects and differs from the standard com.android.library plugin in several key aspects:

  • Single variant architecture: The plugin uses a single variant, removing support for product flavors and build types, which simplifies configuration and enhances build performance.

  • Optimized for KMP: The plugin is designed for KMP libraries, focusing on shared Kotlin code and interoperability, omitting support for Android-specific native builds, AIDL, and RenderScript.

  • Tests disabled by default: Both unit and device (instrumentation) tests are disabled by default to enhance build speed. You can enable them if required.

  • No top-Level Android extension: Configuration is handled with an androidLibrary block within the Gradle KMP DSL, maintaining a consistent KMP project structure. There's no top-level android extension block.

  • Opt-in Java compilation: Java compilation is disabled by default. Use withJava() in the androidLibrary block to enable it. This improves build times when Java compilation is not needed.

Benefits of the Android-KMP library plugin

The Android-KMP plugin provides the following benefits for KMP projects:

  • Improved build performance and stability: It's engineered for optimized build speeds and enhanced stability within KMP projects. It's focus on KMP workflows contribute to a more efficient and reliable build process.

  • Enhanced IDE integration: It provides better code completion, navigation, debugging, and overall developer experience when working with KMP Android libraries.

  • Simplified project configuration: The plugin simplifies configuration for KMP projects by removing Android-specific complexities like build variants. This leads to cleaner and more maintainable build files. Previously, using the com.android.library plugin in KMP project could create confusing source set names, such as androidAndroidTest. This naming convention was less intuitive for developers familiar with standard KMP project structures.

Prerequisites

To use the com.android.kotlin.multiplatform.library plugin, your project must be configured with the following minimum versions or higher:

  • Android Gradle Plugin (AGP): 8.10.0
  • Kotlin Gradle Plugin (KGP): 2.0.0

Apply the Android-KMP plugin to an existing module

To apply the Android-KMP plugin to an existing KMP library module, follow these steps:

  1. Declare plugins in version catalog. Open the version catalog TOML file (usually gradle/libs.versions.toml) and add the plugin definitions section:

    # To check the version number of the latest Kotlin release, go to
    # https://kotlinlang.org/docs/releases.html
    
    [versions]
    androidGradlePlugin = "8.13.0"
    kotlin = "KOTLIN_VERSION"
    
    [plugins]
    kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
    android-kotlin-multiplatform-library = { id = "com.android.kotlin.multiplatform.library", version.ref = "androidGradlePlugin" }
    
  2. Apply the plugin declaration in root build file. Open the build.gradle.kts file located in the root directory of your project. Add the plugin aliases to the plugins block using apply false. This makes the plugin aliases available to all subprojects without applying the plugin logic to the root project itself.

    Kotlin

    // Root build.gradle.kts file
    
    plugins {
       alias(libs.plugins.kotlin.multiplatform) apply false
    
       // Add the following
       alias(libs.plugins.android.kotlin.multiplatform.library) apply false
    }

    Groovy

    // Root build.gradle file
    
    plugins {
       alias(libs.plugins.kotlin.multiplatform) apply false
    
       // Add the following
       alias(libs.plugins.android.kotlin.multiplatform.library) apply false
    }
  3. Apply the plugin in a KMP library module build file. Open the build.gradle.kts file in your KMP library module and apply the plugin at the top of your file within the plugins block:

    Kotlin

    // Module-specific build.gradle.kts file
    
    plugins {
       alias(libs.plugins.kotlin.multiplatform)
    
       // Add the following
       alias(libs.plugins.android.kotlin.multiplatform.library)
    }

    Groovy

    // Module-specific build.gradle file
    
    plugins {
       alias(libs.plugins.kotlin.multiplatform)
    
       // Add the following
       alias(libs.plugins.android.kotlin.multiplatform.library)
    }
  4. Configure Android KMP target. Configure the Kotlin Multiplatform block (kotlin) to define the Android target. Within the kotlin block, specify the Android target using androidLibrary:

    Kotlin

    kotlin {
       androidLibrary {
           namespace = "com.example.kmpfirstlib"
           compileSdk = 33
           minSdk = 24
    
           withJava() // enable java compilation support
           withHostTestBuilder {}.configure {}
           withDeviceTestBuilder {
               sourceSetTreeName = "test"
           }
    
           compilations.configureEach {
               compilerOptions.configure {
                   jvmTarget.set(
                       org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8
                   )
               }
           }
       }
    
       sourceSets {
           androidMain {
               dependencies {
                   // Add Android-specific dependencies here
               }
           }
           getByName("androidHostTest") {
               dependencies {
               }
           }
    
           getByName("androidDeviceTest") {
               dependencies {
               }
           }
       }
       // ... other targets (JVM, iOS, etc.) ...
    }

    Groovy

    kotlin {
       androidLibrary {
           namespace = "com.example.kmpfirstlib"
           compileSdk = 33
           minSdk = 24
    
           withJava() // enable java compilation support
           withHostTestBuilder {}.configure {}
           withDeviceTestBuilder {
               it.sourceSetTreeName = "test"
           }
    
           compilations.configureEach {
               compilerOptions.options.jvmTarget.set(
                   org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8
               )
           }
       }
    
       sourceSets {
           androidMain {
               dependencies {
               }
           }
           androidHostTest {
               dependencies {
               }
           }
           androidDeviceTest {
               dependencies {
               }
           }
       }
       // ... other targets (JVM, iOS, etc.) ...
    }
  5. Apply changes. After applying the plugin and configuring the kotlin block, sync your Gradle project to apply the changes.

Migrate from the legacy plugin

This guide helps you migrate from the legacy com.android.library plugin to the com.android.kotlin.multiplatform.library plugin.

1. Declaring Dependencies

A common task is declaring dependencies for Android-specific source sets. The new plugin requires these to be explicitly placed within the sourceSets block, unlike the general dependencies block used previously.

Android-KMP

The new plugin promotes a cleaner structure by grouping Android dependencies within the androidMain source set. In addition to the main source set, there are two test source sets, which are created on demand: androidDeviceTest and androidHostTest (check the configuring host and device tests for more information).

// build.gradle.kts

kotlin {
    android {}
    //... other targets

    sourceSets {
        commonMain.dependencies {
            implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
        }

        // Dependencies are now scoped to the specific Android source set
        androidMain.dependencies {
            implementation("androidx.appcompat:appcompat:1.7.0")
            implementation("com.google.android.material:material:1.11.0")
        }
    }
}

The source sets have corresponding Kotlin compilations named main, deviceTest, and hostTest. The source sets and compilations can be configured in the build script like so:

// build.gradle.kts

kotlin {
    androidLibrary {
        compilations.getByName("deviceTest") {
            kotlinOptions.languageVersion = "2.0"
        }
    }
}

Legacy Plugin

With the old plugin, you could declare Android-specific dependencies in the top-level dependencies block, which could sometimes be confusing in a multiplatform module.

// build.gradle.kts

kotlin {
  androidTarget()
  //... other targets
}

// Dependencies for all source sets were often mixed in one block
dependencies {
  // Common dependencies
  commonMainImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")

  // Android-specific dependencies
  implementation("androidx.appcompat:appcompat:1.7.0")
  implementation("com.google.android.material:material:1.11.0")
}

2. Enabling Android Resources

Support for Android Resources (res folders) is not enabled by default in the new plugin to optimize build performance. You must opt-in to use them. This change helps ensure that projects not requiring Android-specific resources are not burdened by the associated build overhead.

Android-KMP

You must explicitly enable Android resource processing. The resources should be placed in src/androidMain/res.

// build.gradle.kts

kotlin {
  android {
    // ...
    // Enable Android resource processing
    androidResources {
      enable = true
    }
  }
}

// Project Structure
// └── src
//     └── androidMain
//         └── res
//             ├── values
//             │   └── strings.xml
//             └── drawable
//                 └── icon.xml

Legacy Plugin

Resource processing was enabled by default. You could immediately add a res directory in src/main and start adding XML drawables, values, etc.

// build.gradle.kts

android {
    namespace = "com.example.library"
    compileSdk = 34
    // No extra configuration was needed to enable resources.
}

// Project Structure
// └── src
//     └── main
//         └── res
//             ├── values
//             │   └── strings.xml
//             └── drawable
//                 └── icon.xml

3. Configuring Host and Device Tests

A significant change in the new plugin is that Android host-side (unit) and device-side (instrumented) tests are disabled by default. You must explicitly opt-in to create the test source sets and configurations, whereas the old plugin created them automatically.

This opt-in model helps verify that your project remains lean and only includes the build logic and source sets that you actively use.

Android-KMP

In the new plugin, you enable and configure tests inside the kotlin.android block. This makes the setup more explicit and avoids creating unused test components. The test source set becomes androidHostTest, and androidTest becomes androidDeviceTest.

// build.gradle.kts

kotlin {
  android {
    // ...

    // Opt-in to enable and configure host-side (unit) tests
    withHostTest {
      isIncludeAndroidResources = true
    }

    // Opt-in to enable and configure device-side (instrumented) tests
    withDeviceTest {
      instrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
      execution = "ANDROIDX_TEST_ORCHESTRATOR"
    }
  }
}

// Project Structure (After Opt-in)
// └── src
//     ├── androidHostTest
//     └── androidDeviceTest

Legacy Plugin

With the com.android.library plugin, the test and androidTest source sets were created by default. You would configure their behavior inside the android block, typically using the testOptions DSL.

// build.gradle.kts

android {
  defaultConfig {
    // Runner was configured in defaultConfig
    testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
  }

  testOptions {
    // Configure unit tests (for the 'test' source set)
    unitTests.isIncludeAndroidResources = true

    // Configure device tests (for the 'androidTest' source set)
    execution = "ANDROIDX_TEST_ORCHESTRATOR"
  }
}

// Project Structure (Defaults)
// └── src
//     ├── test
//     └── androidTest

4. Enable Java source compilation

If your KMP library needs to compile Java sources for its Android target, you must explicitly enable this functionality with the new plugin. Note that this enables compilation for Java files located directly within your project, not for its dependencies. The method for setting the Java and Kotlin compiler's JVM target version also changes.

Android-KMP

You must opt-in to Java compilation by calling withJava(). The JVM target is now configured directly inside the kotlin { androidLibrary {} } block for a more unified setup. Setting jvmTarget here applies to both Kotlin and Java compilation for the Android target.

// build.gradle.kts

kotlin {
  android {
    //  Opt-in to enable Java source compilation
    withJava()
    // Configure the JVM target for both Kotlin and Java sources
    compilerOptions {
      jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8)
    }
  }
  // ...
}

// Project Structure:
// └── src
//     └── androidMain
//         ├── kotlin
//         │   └── com/example/MyKotlinClass.kt
//         └── java
//             └── com.example/MyJavaClass.java

Legacy Plugin

Java compilation was enabled by default. The JVM target for both Java and Kotlin sources was set in the android block using compileOptions.

// build.gradle.kts

android {
  // ...
  compileOptions {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
  }
}

kotlin {
  androidTarget {
    compilations.all {
      kotlinOptions.jvmTarget = "1.8"
    }
  }
}

5. Interact with build variants using androidComponents

The androidComponents extension is still available for interacting with build artifacts programmatically. While much of the Variant API remains the same, the new AndroidKotlinMultiplatformVariant interface is more limited because the plugin only produces a single variant.

Consequently, properties related to build types and product flavors are no longer available on the variant object.

Android-KMP

The onVariants block now iterates over a single variant. You can still access common properties like name and artifacts, but not build-type-specific ones.

// build.gradle.kts

androidComponents {
  onVariants { variant ->
      val artifacts = variant.artifacts
  }
}

Legacy Plugin

With multiple variants, you could access build-type-specific properties to configure tasks.

// build.gradle.kts

androidComponents {
  onVariants(selector().withBuildType("release")) { variant ->
    // ...
  }
}

6. Select variants of Android library dependencies

Your KMP library produces a single variant for Android. However, you might depend on a standard Android library (com.android.library) that has multiple variants (e.g., free/paid product flavors). Controlling how your project selects a variant from that dependency is a common requirement.

Android-KMP

The new plugin centralizes and clarifies this logic within the kotlin.android.localDependencySelection block. This makes it much clearer which variants of external dependencies will be selected for your single-variant KMP library.

// build.gradle.kts
kotlin {
  android {
    localDependencySelection {
      // For dependencies with multiple build types, select 'debug' first, and 'release' in case 'debug' is missing
      selectBuildTypeFrom.set(listOf("debug", "release"))

      // For dependencies with a 'type' flavor dimension...
      productFlavorDimension("type") {
        // ...select the 'typeone' flavor.
        selectFrom.set(listOf("typeone"))
      }
    }
  }
}

Legacy Plugin

You configured dependency selection strategies inside the buildTypes and productFlavors blocks. This often involved using missingDimensionStrategy to provide a default flavor for a dimension that your library didn't have, or matchingFallbacks within a specific flavor to define a search order.

Refer to Resolve Matching Errors for more detailed information on the API usage.

Plugin API reference

The new plugin has a different API surface than com.android.library. For detailed information on the new DSL and interfaces, see the API references: