In diesem Dokument finden Plug-in-Autoren eine Anleitung zum korrekten Erkennen, Interagieren mit und Konfigurieren der Kotlin Multiplatform-Einrichtung (KMP). Dabei liegt der Schwerpunkt auf der Integration mit den Android-Zielen in einem KMP-Projekt.
Diese Empfehlungen gelten sowohl für Convention-Plug-ins, mit denen Konfigurationen in den Modulen Ihres Projekts standardisiert werden, als auch für Plug-ins, die für eine breitere Community entwickelt werden. Da sich KMP ständig weiterentwickelt, ist es wichtig, die richtigen Hooks und APIs zu kennen, z. B. KotlinMultiplatformExtension, KotlinTarget-Typen und die Android-spezifischen Integrationsschnittstellen. So können Sie robuste und zukunftssichere Tools entwickeln, die nahtlos auf allen Plattformen funktionieren, die in einem Multiplattformprojekt definiert sind.
Prüfen, ob ein Projekt das Kotlin Multiplatform-Plug-in verwendet
Um Fehler zu vermeiden und dafür zu sorgen, dass Ihr Plug-in nur ausgeführt wird, wenn KMP vorhanden ist, müssen Sie prüfen, ob das Projekt das KMP-Plug-in verwendet. Es empfiehlt sich, plugins.withId() zu verwenden, um auf die Anwendung des KMP-Plug-ins zu reagieren, anstatt sofort danach zu suchen. Mit diesem reaktiven Ansatz wird verhindert, dass Ihr Plug-in von der Reihenfolge abhängt, in der Plug-ins in den Build-Skripts des Nutzers angewendet werden.
import org.gradle.api.Plugin
import org.gradle.api.Project
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.plugins.withId("org.jetbrains.kotlin.multiplatform") {
// The KMP plugin is applied, you can now configure your KMP integration.
}
}
}
Auf das Modell zugreifen
Der Einstiegspunkt für alle Kotlin Multiplatform-Konfigurationen ist die Erweiterung KotlinMultiplatformExtension.
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.plugins.withId("org.jetbrains.kotlin.multiplatform") {
val kmpExtension = project.extensions.getByType(KotlinMultiplatformExtension::class.java)
}
}
}
Auf Kotlin Multiplatform-Ziele reagieren
Verwenden Sie den Container targets, um Ihr Plug-in reaktiv für jedes Ziel zu konfigurieren, das der Nutzer hinzufügt.
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.plugins.withId("org.jetbrains.kotlin.multiplatform") {
val kmpExtension = project.extensions.getByType(KotlinMultiplatformExtension::class.java)
kmpExtension.targets.configureEach { target ->
// 'target' is an instance of KotlinTarget
val targetName = target.name // for example, "android", "iosX64", "jvm"
val platformType = target.platformType // for example, androidJvm, jvm, native, js
}
}
}
}
Zielspezifische Logik anwenden
Wenn Ihr Plug-in Logik nur auf bestimmte Plattformtypen anwenden muss, ist es üblich, die Eigenschaft platformType zu prüfen. Dies ist eine Enumeration, die das Ziel grob kategorisiert.
Verwenden Sie diese Option beispielsweise, wenn Ihr Plug-in nur grob unterscheiden muss (z. B. nur auf JVM-ähnlichen Zielen ausgeführt werden soll):
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.plugins.withId("org.jetbrains.kotlin.multiplatform") {
val kmpExtension = project.extensions.getByType(KotlinMultiplatformExtension::class.java)
kmpExtension.targets.configureEach { target ->
when (target.platformType) {
KotlinPlatformType.jvm -> { /* Standard JVM or Android */ }
KotlinPlatformType.androidJvm -> { /* Android */ }
KotlinPlatformType.js -> { /* JavaScript */ }
KotlinPlatformType.native -> { /* Any Native (iOS, Linux, Windows, etc.) */ }
KotlinPlatformType.wasm -> { /* WebAssembly */ }
KotlinPlatformType.common -> { /* Metadata target (rarely needs direct plugin interaction) */ }
}
}
}
}
}
Android-spezifische Details
Alle Android-Ziele haben den Indikator platformType.androidJvm. KMP hat jedoch zwei unterschiedliche Integrationspunkte, je nachdem, welches Android Gradle-Plug-in verwendet wird: KotlinAndroidTarget für Projekte mit com.android.library oder com.android.application und KotlinMultiplatformAndroidLibraryTarget für Projekte mit com.android.kotlin.multiplatform.library.
Die KotlinMultiplatformAndroidLibraryTarget-API wurde in AGP 8.8.0 hinzugefügt. Wenn
die Nutzer Ihres Plug-ins also eine niedrigere Version von AGP verwenden, kann die Prüfung von
target is KotlinMultiplatformAndroidLibraryTarget zu einer
ClassNotFoundException führen. Um dies zu vermeiden, prüfen Sie AndroidPluginVersion.getCurrent(), bevor Sie den Zieltyp prüfen.
Für AndroidPluginVersion.getCurrent() ist AGP 7.1 oder höher erforderlich.
import com.android.build.api.AndroidPluginVersion
import com.android.build.api.dsl.KotlinMultiplatformAndroidLibraryTarget
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.plugins.withId("com.android.kotlin.multiplatform.library") {
val kmpExtension = project.extensions.getByType(KotlinMultiplatformExtension::class.java)
kmpExtension.targets.configureEach { target ->
if (target is KotlinAndroidTarget) {
// Old kmp android integration using com.android.library or com.android.application
}
if (AndroidPluginVersion.getCurrent() >= AndroidPluginVersion(8, 8) &&
target is KotlinMultiplatformAndroidLibraryTarget
) {
// New kmp android integration using com.android.kotlin.multiplatform.library
}
}
}
}
}
Auf die Android KMP-Erweiterung und ihre Eigenschaften zugreifen
Ihr Plug-in interagiert hauptsächlich mit der Kotlin-Erweiterung, die vom Kotlin Multiplatform-Plug-in bereitgestellt wird, und der Android-Erweiterung, die von AGP für das KMP Android-Ziel bereitgestellt wird. Der Block android {} in der Kotlin-Erweiterung in einem KMP-Projekt wird durch die Schnittstelle KotlinMultiplatformAndroidLibraryTarget dargestellt, die auch KotlinMultiplatformAndroidLibraryExtension erweitert.
So können Sie über dieses einzelne Objekt sowohl auf zielspezifische als auch auf Android-spezifische DSL-Eigenschaften zugreifen.
import com.android.build.api.dsl.KotlinMultiplatformAndroidLibraryTarget
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.plugins.withId("com.android.kotlin.multiplatform.library") {
val kmpExtension = project.extensions.getByType(KotlinMultiplatformExtension::class.java)
// Access the Android target, which also serves as the Android-specific DSL extension
kmpExtension.targets.withType(KotlinMultiplatformAndroidLibraryTarget::class.java).configureEach { androidTarget ->
// You can now access properties and methods from both
// KotlinMultiplatformAndroidLibraryTarget and KotlinMultiplatformAndroidLibraryExtension
androidTarget.compileSdk = 34
androidTarget.namespace = "com.example.myplugin.library"
androidTarget.withJava() // enable Java sources
}
}
}
}
Im Gegensatz zu anderen Android-Plug-ins wie com.android.library oder com.android.application registriert das KMP Android-Plug-in seine Haupt-DSL-Erweiterung nicht auf Projektebene. Sie befindet sich in der KMP-Zielhierarchie, damit sie nur auf das spezifische Android-Ziel angewendet wird, das in Ihrer Multiplattformeinrichtung definiert ist.
Kompilierungen und Quellsätze verarbeiten
Oft müssen Plug-ins auf einer detaillierteren Ebene als nur auf der Ziel-Ebene arbeiten, insbesondere auf der Kompilierungsebene. KotlinMultiplatformAndroidLibraryTarget enthält KotlinMultiplatformAndroidCompilation-Instanzen (z. B. main, hostTest, deviceTest). Jede Kompilierung ist mit Kotlin-Quellsätzen verknüpft. Plug-ins können mit diesen interagieren, um Quellen oder Abhängigkeiten hinzuzufügen oder Kompilierungsaufgaben zu konfigurieren.
import com.android.build.api.dsl.KotlinMultiplatformAndroidCompilation
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.plugins.withId("com.android.kotlin.multiplatform.library") {
val kmpExtension = project.extensions.getByType(KotlinMultiplatformExtension::class.java)
kmpExtension.targets.configureEach { target ->
target.compilations.configureEach { compilation ->
// standard compilations are usually 'main' and 'test'
// android target has 'main', 'hostTest', 'deviceTest'
val compilationName = compilation.name
// Access the default source set for this compilation
val defaultSourceSet = compilation.defaultSourceSet
// Access the Android-specific compilation DSL
if (compilation is KotlinMultiplatformAndroidCompilation) {
}
// Access and configure the Kotlin compilation task
compilation.compileTaskProvider.configure { compileTask ->
}
}
}
}
}
}
Testkompilierungen in Convention-Plug-ins konfigurieren
Wenn Sie Standardwerte für Testkompilierungen (z. B. targetSdk für instrumentierte Tests) in einem Convention-Plug-in konfigurieren, sollten Sie keine Aktivierungsmethoden wie withDeviceTest { } oder withHostTest { } verwenden. Wenn Sie diese Methoden sofort aufrufen, wird die Erstellung der entsprechenden Android-Testvarianten und -Kompilierungen für jedes Modul ausgelöst, das das Convention-Plug-in anwendet. Das ist möglicherweise nicht geeignet. Außerdem können diese Methoden in einem bestimmten Modul nicht ein zweites Mal aufgerufen werden, um Einstellungen zu verfeinern. Andernfalls wird ein Fehler ausgegeben, dass die Kompilierung bereits erstellt wurde.
Wir empfehlen stattdessen, einen reaktiven configureEach-Block für den Container „compilations“ zu verwenden. So können Sie Standardkonfigurationen bereitstellen, die nur angewendet werden, wenn ein Modul die Testkompilierung explizit aktiviert:
import com.android.build.api.dsl.KotlinMultiplatformAndroidDeviceTestCompilation
import com.android.build.api.dsl.KotlinMultiplatformAndroidLibraryTarget
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.plugins.withId("com.android.kotlin.multiplatform.library") {
val kmpExtension =
project.extensions.getByType(KotlinMultiplatformExtension::class.java)
kmpExtension.targets.withType(KotlinMultiplatformAndroidLibraryTarget::class.java)
.configureEach { androidTarget ->
androidTarget.compilations.withType(
KotlinMultiplatformAndroidDeviceTestCompilation::class.java
).configureEach {
targetSdk { version = release(34) }
}
}
}
}
}
Mit diesem Muster wird sichergestellt, dass Ihr Convention-Plug-in verzögert bleibt und einzelne Module withDeviceTest { } aufrufen können, um ihre Tests zu aktivieren und weiter anzupassen, ohne mit den Standardeinstellungen in Konflikt zu geraten.
Mit der Variant API interagieren
Für Aufgaben, die eine Konfiguration in der Spätphase, den Zugriff auf Artefakte (z. B. Manifeste oder Bytecode) oder die Möglichkeit erfordern, bestimmte Komponenten zu aktivieren oder zu deaktivieren, müssen Sie die Android Variant API verwenden. In KMP-Projekten hat die Erweiterung den Typ KotlinMultiplatformAndroidComponentsExtension.
Die Erweiterung wird auf Projektebene registriert, wenn das KMP Android-Plug-in angewendet wird.
Verwenden Sie beforeVariants, um die Erstellung von Varianten oder deren verschachtelten Testkomponenten (hostTests und deviceTests) zu steuern. Hier können Sie Tests programmatisch deaktivieren oder die Werte von DSL-Eigenschaften ändern.
import com.android.build.api.variant.KotlinMultiplatformAndroidComponentsExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.plugins.withId("com.android.kotlin.multiplatform.library") {
val androidComponents = project.extensions.findByType(KotlinMultiplatformAndroidComponentsExtension::class.java)
androidComponents?.beforeVariants { variantBuilder ->
// Disable all tests for this module
variantBuilder.hostTests.values.forEach { it.enable = false }
variantBuilder.deviceTests.values.forEach { it.enable = false }
}
}
}
}
Verwenden Sie onVariants, um auf das endgültige Variant-Objekt (KotlinMultiplatformAndroidVariant) zuzugreifen. Hier können Sie aufgelöste Eigenschaften prüfen oder Transformationen für Artefakte wie das zusammengeführte Manifest oder Bibliotheksklassen registrieren.
import com.android.build.api.variant.KotlinMultiplatformAndroidComponentsExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.plugins.withId("com.android.kotlin.multiplatform.library") {
val androidComponents = project.extensions.findByType(KotlinMultiplatformAndroidComponentsExtension::class.java)
androidComponents?.onVariants { variant ->
// 'variant' is a KotlinMultiplatformAndroidVariant
val variantName = variant.name
// Access the artifacts API
val manifest = variant.artifacts.get(com.android.build.api.variant.SingleArtifact.MERGED_MANIFEST)
}
}
}
}
Empfehlungen
- Hinweis: Linktext wird angezeigt, wenn JavaScript deaktiviert ist
- Umgebung einrichten
- KMP-Modul zu einem Projekt hinzufügen
- Android Gradle Library-Plug-in für KMP einrichten