为 KMP 设置 Android Gradle 库插件

com.android.kotlin.multiplatform.library Gradle 插件是用于向 Kotlin Multiplatform (KMP) 库模块添加 Android 目标平台的官方支持工具。它简化了项目配置,提高了 build 性能,并可更好地与 Android Studio 集成。

之前的做法现已弃用,取而代之的是插件(也称为 Android-KMP 插件)。继续使用 KMP 的 com.android.library 插件将不再受 JetBrains 支持,并且无法受益于未来的更新和改进。

如需应用此插件,请参阅应用 Android-KMP 插件部分。如果您需要从旧版 API 迁移,请参阅迁移指南

主要功能和区别

Android-KMP 插件专门针对 KMP 项目量身打造,在几个关键方面与标准 com.android.library 插件有所不同:

  • 单变体架构:插件使用单个变体,不再支持产品变种和 build 类型,从而简化配置并提升 build 性能。

  • 针对 KMP 进行了优化:该插件专为 KMP 库设计,侧重于共享 Kotlin 代码和互操作性,省略了对特定于 Android 的原生 build、AIDL 和 RenderScript 的支持。

  • 默认停用的测试:默认情况下,单元测试和设备(插桩)测试均处于停用状态,以提高 build 速度。您可以根据需要启用它们。

  • 没有顶级 Android 扩展程序:配置通过 Gradle KMP DSL 中的 androidLibrary 代码块处理,从而保持一致的 KMP 项目结构。没有顶级 android 扩展块。

  • 选择启用 Java 编译:Java 编译默认处于停用状态。在 androidLibrary 块中使用 withJava() 即可启用该功能。这样可以缩短不需要 Java 编译时的构建时间。

Android-KMP 库插件的优势

Android-KMP 插件可为 KMP 项目带来以下好处:

  • 提升了构建性能和稳定性:它经过精心设计,可优化 KMP 项目中的构建速度并提高稳定性。它专注于 KMP 工作流,有助于提高构建流程的效率和可靠性。

  • 增强的 IDE 集成:在处理 KMP Android 库时,可提供更好的代码补全、导航、调试和整体开发者体验。

  • 简化的项目配置:该插件通过移除特定于 Android 的复杂性(例如 build 变体)来简化 KMP 项目的配置。这有助于生成更简洁且更易于维护的 build 文件。 之前,在 KMP 项目中使用 com.android.library 插件可能会创建令人困惑的源代码集名称,例如 androidAndroidTest。对于熟悉标准 KMP 项目结构的开发者来说,这种命名惯例不太直观。

前提条件

如需使用 com.android.kotlin.multiplatform.library 插件,您的项目必须配置为以下最低版本或更高版本:

  • Android Gradle 插件 (AGP):8.10.0
  • Kotlin Gradle 插件 (KGP):2.0.0

将 Android-KMP 插件应用于现有模块

如需将 Android-KMP 插件应用于现有的 KMP 库模块,请按以下步骤操作:

  1. 在版本目录中声明插件。打开版本目录 TOML 文件(通常为 gradle/libs.versions.toml),然后添加插件定义部分:

    # To check the version number of the latest Kotlin release, go to
    # https://kotlinlang.org/docs/releases.html
    
    [versions]
    androidGradlePlugin = "8.12.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. 在根 build 文件中应用插件声明。打开位于项目根目录中的 build.gradle.kts 文件。使用 apply false 将插件别名添加到 plugins 代码块。这样一来,插件别名便可供所有子项目使用,而无需将插件逻辑应用于根项目本身。

    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. 在 KMP 库模块 build 文件中应用插件。打开 KMP 库模块中的 build.gradle.kts 文件,并在 plugins 块内的文件顶部应用插件:

    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. 配置 Android KMP 目标平台。配置 Kotlin Multiplatform 块 (kotlin) 以定义 Android 目标平台。在 kotlin 块内,使用 androidLibrary 指定 Android 目标平台:

    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. 应用更改。应用插件并配置 kotlin 块后,同步 Gradle 项目以应用更改。

从旧版插件迁移

本指南可帮助您从旧版 com.android.library 插件迁移到 com.android.kotlin.multiplatform.library 插件。

1. 声明依赖项

一项常见任务是为特定于 Android 的源代码集声明依赖项。新插件要求将这些内容明确放置在 sourceSets 块中,这与之前使用的常规 dependencies 块不同。

Android-KMP

新插件通过将 Android 依赖项分组到 androidMain 源代码集中,从而实现更清晰的结构。除了主源代码集之外,还有两个按需创建的测试源代码集:androidDeviceTestandroidHostTest(如需了解详情,请参阅配置主机和设备测试)。

// 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")
        }
    }
}

这些源集具有相应的 Kotlin 编译,分别命名为 maindeviceTesthostTest。可以在 build 脚本中配置源集和编译,如下所示:

// build.gradle.kts

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

旧版插件

使用旧版插件时,您可以在顶级依赖项代码块中声明特定于 Android 的依赖项,这有时会在多平台模块中造成混淆。

// 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. 启用 Android 资源

为了优化 build 性能,新插件默认情况下未启用对 Android 资源(res 文件夹)的支持。您必须选择启用才能使用这些功能。此更改有助于确保不需要 Android 专属资源的项目不会因相关的 build 开销而受到影响。

Android-KMP

您必须明确启用 Android 资源处理。资源应放置在 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

旧版插件

资源处理默认处于启用状态。您可以立即在 src/main 中添加 res 目录,并开始添加 XML 可绘制对象、值等。

// 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. 配置主机和设备测试

新插件的一项重大变更是,默认情况下,Android 主机端(单元)测试和设备端(插桩)测试处于停用状态。您必须明确选择启用才能创建测试源代码集和配置,而旧版插件会自动创建它们。

这种选择启用模式有助于验证您的项目是否保持精简,并且仅包含您主动使用的 build 逻辑和源集。

Android-KMP

在新插件中,您可以在 kotlin.android 代码块内启用和配置测试。这样可以使设置更加明确,并避免创建未使用的测试组件。test 源集变为 androidHostTestandroidTest 变为 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

旧版插件

使用 com.android.library 插件时,系统默认创建了 testandroidTest 来源集。您可以在 android 块内配置其行为,通常使用 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. 启用 Java 源代码编译

如果您的 KMP 库需要为其 Android 目标平台编译 Java 源代码,您必须使用新插件明确启用此功能。请注意,这会启用对直接位于项目中的 Java 文件(而非其依赖项)的编译。设置 Java 和 Kotlin 编译器 JVM 目标版本的方法也会发生变化。

Android-KMP

您必须通过调用 withJava() 选择启用 Java 编译。现在,JVM 目标直接在 kotlin { androidLibrary {} } 代码块内配置,以实现更统一的设置。在此处设置 jvmTarget 会同时应用于 Android 目标的 Kotlin 和 Java 编译。

// 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

旧版插件

Java 编译默认处于启用状态。Java 和 Kotlin 源的 JVM 目标是在 android 块中使用 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. 使用 androidComponents 与 build 变体互动

您仍然可以使用 androidComponents 扩展以编程方式与 build 制品进行交互。虽然大部分 Variant API 保持不变,但新的 AndroidKotlinMultiplatformVariant 接口更加受限,因为插件仅生成一个变体。

因此,与 build 类型和产品变种相关的属性不再在变体对象上提供。

Android-KMP

onVariants 块现在会针对单个变体进行迭代。您仍然可以访问 nameartifacts 等常见属性,但无法访问特定于 build 类型的属性。

// build.gradle.kts

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

旧版插件

借助多个变体,您可以访问特定于 build 类型的属性来配置任务。

// build.gradle.kts

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

6. 选择 Android 库依赖项的变体

您的 KMP 库为 Android 生成单个变体。不过,您可能依赖于具有多个变体的标准 Android 库 (com.android.library)(例如 free/paid 产品变种)。控制项目如何从相应依赖项中选择变体是一项常见要求。

Android-KMP

新插件将此逻辑集中并明确地放在 kotlin.android.localDependencySelection 块中。这样一来,您就可以更清楚地了解将为单变体 KMP 库选择哪些外部依赖项变体。

// 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"))
      }
    }
  }
}

旧版插件

您在 buildTypes and productFlavors 块内配置了依赖项选择策略。这通常涉及使用 missingDimensionStrategy 为库中没有的维度提供默认变种,或在特定变种中使用 matchingFallbacks 定义搜索顺序。

如需详细了解 API 用法,请参阅解决匹配错误

插件 API 参考文档

新插件的 API Surface 与 com.android.library 不同。如需详细了解新的 DSL 和接口,请参阅以下 API 参考文档: