Questo documento fornisce una guida per gli autori di plug-in su come rilevare, interagire e configurare correttamente la configurazione di Kotlin Multiplatform (KMP), con un focus specifico sull'integrazione con i target Android all'interno di un progetto KMP. Man mano che KMP continua a evolversi, comprendere gli hook e le API corretti, come i tipi KotlinMultiplatformExtension e KotlinTarget e le interfacce di integrazione specifiche di Android, è essenziale per creare strumenti solidi e a prova di futuro che funzionino perfettamente su tutte le piattaforme definite in un progetto multipiattaforma.
Verificare se un progetto utilizza il plug-in Kotlin Multiplatform
Per evitare errori e assicurarti che il plug-in venga eseguito solo quando è presente KMP,
devi verificare se il progetto utilizza il plug-in KMP. La best practice consiste nell'utilizzare
plugins.withId() per reagire all'applicazione del plug-in KMP, anziché
controllarlo immediatamente. Questo approccio reattivo impedisce al plug-in di
essere fragile rispetto all'ordine in cui i plug-in vengono applicati negli script di build dell'utente.
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.
}
}
}
Accedere al modello
Il punto di ingresso per tutte le configurazioni di Kotlin Multiplatform è l'estensione
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)
}
}
}
Reagire ai target multipiattaforma Kotlin
Utilizza il contenitore targets per configurare in modo reattivo il plug-in per ogni target
aggiunto dall'utente.
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
}
}
}
}
Applica una logica specifica per la destinazione
Se il tuo plug-in deve applicare la logica solo a determinati tipi di piattaforme, un approccio comune è controllare la proprietà platformType. Si tratta di un'enumerazione che classifica
in modo generico il target.
Ad esempio, utilizzalo se il plug-in deve solo differenziare in modo generico (ad esempio, eseguire solo su target simili alla JVM):
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) */ }
}
}
}
}
}
Dettagli specifici per Android
Mentre tutti i target Android hanno l'indicatore platformType.androidJvm, KMP ha
due punti di integrazione distinti a seconda del plug-in Android Gradle utilizzato:
KotlinAndroidTarget per i progetti che utilizzano com.android.library o
com.android.application e KotlinMultiplatformAndroidLibraryTarget per
i progetti che utilizzano com.android.kotlin.multiplatform.library.
L'API KotlinMultiplatformAndroidLibraryTarget è stata aggiunta in AGP 8.8.0, quindi se
i consumatori del tuo plug-in utilizzano una versione precedente di AGP, il controllo di
target is KotlinMultiplatformAndroidLibraryTarget potrebbe generare un
ClassNotFoundException. Per rendere questa operazione sicura, controlla
AndroidPluginVersion.getCurrent() prima di controllare il tipo di target.
Tieni presente che AndroidPluginVersion.getCurrent() richiede AGP 7.1 o versioni successive.
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
}
}
}
}
}
Accedere all'estensione KMP di Android e alle relative proprietà
Il plug-in interagirà principalmente con l'estensione Kotlin fornita
dal plug-in Kotlin Multiplatform e con l'estensione Android fornita da AGP
per il target Android KMP. Il blocco android {} all'interno dell'estensione Kotlin
in un progetto KMP è rappresentato dall'interfaccia
KotlinMultiplatformAndroidLibraryTarget, che estende anche KotlinMultiplatformAndroidLibraryExtension.
Ciò significa che puoi accedere alle proprietà DSL specifiche per il target e per Android tramite questo singolo oggetto.
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
}
}
}
}
A differenza di altri plug-in Android (come com.android.library o
com.android.application), il plug-in KMP Android non registra la sua estensione DSL principale a livello di progetto. Si trova all'interno della gerarchia dei target KMP
per assicurarsi che venga applicato solo al target Android specifico definito nella
configurazione multipiattaforma.
Gestire le compilation e i set di fonti
Spesso, i plug-in devono operare a un livello più granulare rispetto al solo
target, in particolare devono operare a livello di compilazione. KotlinMultiplatformAndroidLibraryTarget
contiene KotlinMultiplatformAndroidCompilation istanze (ad esempio main,
hostTest, deviceTest). Ogni compilazione è associata a set di origini Kotlin. I plug-in possono interagire con questi per aggiungere origini, dipendenze o configurare
attività di compilazione.
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 ->
}
}
}
}
}
}
Configurare le compilazioni di test nei plug-in delle convenzioni
Quando configuri i valori predefiniti per le compilazioni di test (ad esempio targetSdk per i test strumentati) in un plug-in di convenzione, devi evitare di utilizzare metodi di attivazione come withDeviceTest { } o withHostTest { }. La chiamata di questi metodi
attiva immediatamente la creazione delle varianti di test e delle
compilazioni Android corrispondenti per ogni modulo che applica il plug-in di convenzione, il che potrebbe
non essere adatto. Inoltre, questi metodi non possono essere chiamati una seconda volta in un modulo specifico per perfezionare le impostazioni, perché in questo modo verrà generato un errore che indica che la compilazione è già stata creata.
Ti consigliamo invece di utilizzare un blocco configureEach reattivo
nel contenitore delle compilation. In questo modo puoi fornire configurazioni predefinite
che vengono applicate solo se e quando un modulo attiva esplicitamente la compilazione del test:
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) }
}
}
}
}
}
Questo pattern assicura che il plug-in delle convenzioni rimanga pigro e consenta
ai singoli moduli di chiamare withDeviceTest { } per attivare e personalizzare ulteriormente
i test senza entrare in conflitto con i valori predefiniti.
Interagire con l'API Variant
Per le attività che richiedono la configurazione in fase avanzata, l'accesso agli artefatti (come
manifest o bytecode) o la possibilità di attivare o disattivare componenti
specifici, devi utilizzare l'API Android Variant. Nei progetti KMP, l'estensione è di tipo KotlinMultiplatformAndroidComponentsExtension.
L'estensione viene registrata a livello di progetto quando viene applicato il plug-in KMP Android.
Utilizza beforeVariants per controllare la creazione di varianti o dei relativi componenti di test nidificati (hostTests e deviceTests). Questo è il posto giusto per disattivare i test a livello di programmazione o modificare i valori delle proprietà DSL.
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 }
}
}
}
}
Utilizza onVariants per accedere all'oggetto variante finale
(KotlinMultiplatformAndroidVariant). Qui puoi esaminare le proprietà risolte
o registrare le trasformazioni su artefatti come il manifest unito
o le classi della libreria.
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)
}
}
}
}
Consigliati per te
- Nota: il testo del link viene visualizzato quando JavaScript è disattivato
- Configura l'ambiente
- Aggiungi il modulo KMP a un progetto
- Configura il plug-in della libreria Android Gradle per KMP