Dieses Dokument enthält einen Leitfaden für Plug-in-Autoren, wie sie die Kotlin Multiplatform (KMP)-Konfiguration richtig erkennen, mit ihr interagieren und sie konfigurieren können. Ein besonderer Schwerpunkt liegt dabei auf der Integration mit den Android-Zielen innerhalb eines KMP-Projekts.
Diese Empfehlungen gelten unabhängig davon, ob Sie Konventions-Plug-ins erstellen, um Konfigurationen in den Modulen Ihres Projekts zu standardisieren, oder Plug-ins für die allgemeine Verwendung in der Community entwickeln. 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. Nur so können Sie robuste und zukunftssichere Tools entwickeln, die nahtlos auf allen Plattformen funktionieren, die in einem Multiplattform-Projekt 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, mit plugins.withId() auf die Anwendung des KMP-Plug-ins zu reagieren, anstatt es sofort zu prüfen. Dieser reaktive Ansatz verhindert, dass Ihr Plug-in empfindlich auf die Reihenfolge reagiert, 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 targets-Container, um Ihr Plug-in für jedes vom Nutzer hinzugefügte Ziel reaktiv zu konfigurieren.
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 Property platformType zu prüfen. Dies ist ein Enum, das 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 */ }
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, aber KMP hat zwei unterschiedliche Integrationspunkte, je nachdem, welches Android Gradle-Plug-in verwendet wird: KotlinAndroidTarget für Projekte, die com.android.library oder com.android.application verwenden, und KotlinMultiplatformAndroidLibraryTarget für Projekte, die com.android.kotlin.multiplatform.library verwenden.
Die KotlinMultiplatformAndroidLibraryTarget API wurde in AGP 8.8.0 hinzugefügt. Wenn die Nutzer Ihres Plug-ins eine niedrigere AGP-Version verwenden, kann die Prüfung von target is KotlinMultiplatformAndroidLibraryTarget zu einem ClassNotFoundException führen. Prüfen Sie AndroidPluginVersion.getCurrent(), bevor Sie den Zieltyp prüfen, um dies sicher zu machen.
Für AndroidPluginVersion.getCurrent() ist AGP 8.3.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.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
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 ->
if (target is KotlinAndroidTarget) {
// Old kmp android integration using com.android.library or com.android.application
} else if (
// Check platformType first, so AGP classes load only for Android targets
target.platformType == KotlinPlatformType.androidJvm &&
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 vom AGP für das KMP-Android-Ziel bereitgestellt wird. Der android {}-Block innerhalb der Kotlin-Erweiterung in einem KMP-Projekt wird durch die KotlinMultiplatformAndroidLibraryTarget-Schnittstelle dargestellt, die auch KotlinMultiplatformAndroidLibraryExtension erweitert.
Das bedeutet, dass du über dieses einzelne Objekt sowohl auf zielspezifische als auch auf Android-spezifische DSL-Eigenschaften zugreifen kannst.
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 (z. B. 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 Multiplattform-Konfiguration definiert ist.
Kompilierungen und Source-Sets verarbeiten
Häufig müssen Plug-ins auf einer detaillierteren Ebene als nur auf der Ziel-Ebene arbeiten, insbesondere auf der Kompilierungsebene. Die KotlinMultiplatformAndroidLibraryTarget enthält KotlinMultiplatformAndroidCompilation-Instanzen (z. B. main, hostTest, deviceTest). Jede Kompilierung ist mit Kotlin-Source-Sets 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 Konventions-Plug-ins konfigurieren
Wenn Sie Standardwerte für Testkompilierungen (z. B. targetSdk für Instrumentierungstests) in einem Konventions-Plug-in konfigurieren, sollten Sie keine Aktivierungsmethoden wie withDeviceTest { } oder withHostTest { } verwenden. Wenn Sie diese Methoden aufrufen, wird die Erstellung der entsprechenden Android-Testvarianten und ‑Kompilierungen für jedes Modul, das das Konventions-Plug-in anwendet, sofort ausgelöst. Das ist möglicherweise nicht gewünscht. Außerdem können diese Methoden nicht ein zweites Mal in einem bestimmten Modul aufgerufen werden, um die Einstellungen zu verfeinern, da dies zu einem Fehler führt, der besagt, dass die Kompilierung bereits erstellt wurde.
Stattdessen empfehlen wir, einen reaktiven configureEach-Block im Kompilierungscontainer zu verwenden. So können Sie Standardkonfigurationen bereitstellen, die nur gelten, 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 {
it.targetSdk { version = release(34) }
}
}
}
}
}
Dieses Muster sorgt dafür, dass Ihr Konventions-Plug-in Lazy bleibt und einzelne Module withDeviceTest { } aufrufen können, um ihre Tests zu aktivieren und weiter anzupassen, ohne dass es zu Konflikten mit den Standardeinstellungen kommt.
Mit der Variant API interagieren
Für Aufgaben, die eine Konfiguration in einer späten Phase, Zugriff auf Artefakte (z. B. Manifeste oder Bytecode) oder die Möglichkeit zum Aktivieren oder Deaktivieren bestimmter Komponenten erfordern, müssen Sie die Android Variant API verwenden. In KMP-Projekten ist die Erweiterung vom 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 Variantenobjekt (KotlinMultiplatformAndroidVariant) zuzugreifen. Hier können Sie aufgelöste Attribute 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.artifact.SingleArtifact.MERGED_MANIFEST)
}
}
}
}
Empfehlungen für Sie
- Hinweis: Der 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