Dieses Dokument enthält eine Anleitung für Plug-in-Autoren, wie sie die Kotlin Multiplatform-Konfiguration (KMP) richtig erkennen, mit ihr interagieren und sie konfigurieren können. Der Schwerpunkt liegt dabei auf der Integration in die Android-Ziele in einem KMP-Projekt. Da KMP sich 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 darauf zu reagieren, dass das KMP-Plug-in angewendet wird, anstatt sofort danach zu suchen. Dieser reaktive Ansatz verhindert, dass Ihr Plug-in anfällig für die Reihenfolge ist, 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 KotlinMultiplatformExtension-Erweiterung.
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
Mit dem targets-Container können Sie Ihr Plug-in reaktiv für jedes Ziel 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
}
}
}
}
Zielgruppenspezifische Logik anwenden
Wenn Ihr Plug-in die Logik nur auf bestimmte Arten von Plattformen anwenden muss, ist es üblich, die platformType-Eigenschaft zu prüfen. Dies ist ein Enum, das das Ziel grob kategorisiert.
Verwenden Sie diese Option beispielsweise, wenn Ihr Plug-in nur eine grobe Unterscheidung vornehmen 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
Während alle Android-Ziele den Indikator platformType.androidJvm haben, gibt es für KMP zwei verschiedene 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 eine niedrigere Version von AGP verwenden, kann die Prüfung von target is KotlinMultiplatformAndroidLibraryTarget zu einem ClassNotFoundException führen. Um dies sicher zu machen, 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 android {}-Block in der Kotlin-Erweiterung eines KMP-Projekts wird durch die KotlinMultiplatformAndroidLibraryTarget-Schnittstelle dargestellt, die auch KotlinMultiplatformAndroidLibraryExtension erweitert.
Das bedeutet, dass Sie über dieses einzelne Objekt sowohl auf zielgruppenspezifische als auch auf Android-spezifische DSL-Properties zugreifen können.
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) wird die DSL-Haupterweiterung des KMP-Android-Plug-ins nicht auf Projektebene registriert. Sie befindet sich in der KMP-Zielhierarchie, damit sie nur für das spezifische Android-Ziel gilt, das in Ihrer Multiplattformkonfiguration definiert ist.
Zusammenstellungen und Quellsets verarbeiten
Häufig müssen Plug-ins auf einer detaillierteren Ebene als nur dem Ziel funktionieren, insbesondere auf der Ebene der Kompilierung. Die KotlinMultiplatformAndroidLibraryTarget enthält KotlinMultiplatformAndroidCompilation-Instanzen (z. B. main, hostTest, deviceTest). Jede Kompilierung ist mit Kotlin-Quellsets verknüpft. Plugins 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 ->
}
}
}
}
}
}
Test-Kompilierungen in Convention-Plug-ins konfigurieren
Wenn Sie Standardwerte für Test-Builds (z. B. targetSdk für instrumentierte Tests) in einem Convention-Plugin konfigurieren, sollten Sie keine Enabler-Methoden 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, für das das Convention-Plug-in gilt. Das ist möglicherweise nicht geeignet. Außerdem können diese Methoden nicht ein zweites Mal in einem bestimmten Modul aufgerufen werden, um die Einstellungen zu optimieren, da sonst ein Fehler ausgegeben wird, der besagt, dass die Kompilierung bereits erstellt wurde.
Wir empfehlen stattdessen, im Container für Zusammenstellungen einen reaktiven configureEach-Block 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) }
}
}
}
}
}
Dieses Muster sorgt dafür, dass Ihr Konventions-Plug-in verzögert geladen wird 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 späte Konfiguration, 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-Plugin angewendet wird.
Mit beforeVariants können Sie die Erstellung von Varianten oder deren verschachtelten Testkomponenten (hostTests und deviceTests) 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 für dich
- Hinweis: Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- Umgebung einrichten
- KMP-Modul zu einem Projekt hinzufügen
- Android-Gradle-Bibliotheks-Plug-in für KMP einrichten