Questa guida spiega come rilevare, interagire e configurare il target Android in un progetto Kotlin Multiplatform (KMP) da un plug-in Gradle personalizzato. Utilizza queste API quando scrivi plug-in di convenzione per la tua build o pubblichi plug-in Gradle per i progetti KMP.
Verifica se un progetto utilizza il plug-in Kotlin Multiplatform
Per evitare errori e assicurarti che il tuo plug-in venga eseguito solo quando è presente KMP, devi verificare se il progetto utilizza il plug-in KMP. La best practice è utilizzare
plugins.withId() per reagire all'applicazione del plug-in KMP, anziché
verificarlo immediatamente. Questo approccio reattivo impedisce al plug-in di essere vulnerabile 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.
}
}
}
Accedi al modello
Il punto di ingresso per tutte le configurazioni 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)
}
}
}
Reagisci alle destinazioni Kotlin Multiplatform
Utilizza il container 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 la 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 enum che classifica
in modo generale il target.
Ad esempio, utilizza questa opzione se il tuo plug-in deve solo distinguere in modo generale (ad esempio, l'esecuzione solo su target simili a 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 */ }
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
Sebbene tutte le destinazioni Android abbiano l'indicatore platformType.androidJvm, KMP ha
due punti di integrazione distinti a seconda del plug-in Android per 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 consumer del tuo plug-in utilizzano una versione precedente di AGP, il controllo di target is KotlinMultiplatformAndroidLibraryTarget potrebbe generare un ClassNotFoundException. Per farlo in sicurezza, controlla AndroidPluginVersion.getCurrent() prima di controllare il tipo di destinazione.
AndroidPluginVersion.getCurrent() richiede AGP 8.3.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.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
}
}
}
}
}
Accedi all'estensione KMP per Android e alle relative proprietà
Il plug-in interagisce 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 la destinazione 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 Android KMP non registra la sua estensione
DSL principale a livello di progetto. Si trova all'interno della gerarchia di destinazione KMP per garantire che venga applicato solo al target Android specifico definito nella configurazione multipiattaforma.
Gestisci le compilazioni e i set di risorse
Spesso, i plug-in devono funzionare a un livello più granulare rispetto al semplice target, in particolare a livello di compilazione. KotlinMultiplatformAndroidLibraryTarget contiene istanze KotlinMultiplatformAndroidCompilation (ad esempio main, hostTest, deviceTest). Ogni compilazione è associata a set di risorse Kotlin. I plug-in possono interagire con questi elementi 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 ->
}
}
}
}
}
}
Configura le compilazioni di test nei plug-in di convenzione
Quando configuri i valori predefiniti per le compilazioni di test (ad esempio targetSdk per i test di instrumentazione) in un plug-in di convenzione, evita di utilizzare metodi di attivazione come withDeviceTest { } o withHostTest { }. La chiamata di questi metodi attiva la creazione delle varianti di test Android corrispondenti e delle compilazioni per ogni modulo che applica il plug-in della 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é ciò genera un errore che indica che la compilazione è già stata creata.
Utilizza invece un blocco configureEach reattivo nel container delle compilation.
In questo modo puoi fornire configurazioni predefinite che si applicano 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 {
it.targetSdk { version = release(34) }
}
}
}
}
}
Questo pattern garantisce che il plug-in di convenzione rimanga lazy e consente ai singoli moduli di chiamare withDeviceTest { } per attivare e personalizzare ulteriormente i test senza entrare in conflitto con i valori predefiniti.
Interagisci con l'API Variant
Per le attività che richiedono una 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 Android KMP.
Utilizza beforeVariants per controllare la creazione di varianti o dei relativi componenti di test nidificati (hostTests e deviceTests). Questo è il punto corretto per disattivare i test in modo programmatico 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 della variante finale (KotlinMultiplatformAndroidVariant). Qui puoi ispezionare 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.artifact.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 plugin della libreria Android Gradle per KMP