En esta guía, se explica cómo detectar, interactuar y configurar el destino de Android en un proyecto de Kotlin Multiplatform (KMP) desde un complemento de Gradle personalizado. Usa estas APIs cuando escribas complementos de convenciones para tu compilación o publiques complementos de Gradle para proyectos de KMP.
Cómo verificar si un proyecto usa el complemento de Kotlin Multiplatform
Para evitar errores y asegurarte de que tu complemento solo se ejecute cuando KMP esté presente,
debes verificar si el proyecto usa el complemento de KMP. Se recomienda usar plugins.withId() para reaccionar a la aplicación del complemento de KMP, en lugar de verificarlo de inmediato. Este enfoque reactivo evita que tu complemento sea frágil en el orden en que se aplican los complementos en las secuencias de comandos de compilación del usuario.
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.
}
}
}
Accede al modelo
El punto de entrada para todas las configuraciones de Kotlin Multiplatform es la extensión 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)
}
}
}
Cómo reaccionar a los destinos de Kotlin Multiplatform
Usa el contenedor targets para configurar de forma reactiva tu complemento para cada destino que agregue el usuario.
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
}
}
}
}
Aplica lógica específica para el destino
Si tu complemento necesita aplicar lógica solo a ciertos tipos de plataformas, un enfoque común es verificar la propiedad platformType. Esta es una enumeración que categoriza
el objetivo de forma general.
Por ejemplo, usa esto si tu complemento solo necesita diferenciar de forma general (como ejecutarse solo en destinos similares 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) */ }
}
}
}
}
}
Detalles específicos de Android
Si bien todos los destinos de Android tienen el indicador platformType.androidJvm, KMP tiene
dos puntos de integración distintos según el complemento de Android para Gradle que se use:
KotlinAndroidTarget para los proyectos que usan com.android.library o
com.android.application, y KotlinMultiplatformAndroidLibraryTarget para
los proyectos que usan com.android.kotlin.multiplatform.library.
La API de KotlinMultiplatformAndroidLibraryTarget se agregó en AGP 8.8.0, por lo que, si
los consumidores de tu complemento ejecutan una versión anterior de AGP, la verificación de
target is KotlinMultiplatformAndroidLibraryTarget podría generar un
ClassNotFoundException. Para que esto sea seguro, verifica AndroidPluginVersion.getCurrent() antes de verificar el tipo de destino.
AndroidPluginVersion.getCurrent() requiere AGP 8.3.1 o una versión posterior.
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
}
}
}
}
}
Cómo acceder a la extensión de KMP para Android y sus propiedades
Tu complemento interactúa principalmente con la extensión de Kotlin que proporciona el complemento de Kotlin Multiplatform y la extensión de Android que proporciona el AGP para el destino de Android de KMP. El bloque android {} dentro de la extensión de Kotlin
en un proyecto de KMP se representa con la interfaz KotlinMultiplatformAndroidLibraryTarget,
que también extiende KotlinMultiplatformAndroidLibraryExtension.
Esto significa que puedes acceder a las propiedades de DSL específicas del destino y de Android a través de este único objeto.
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 diferencia de otros complementos para Android (como com.android.library o com.android.application), el complemento para Android de KMP no registra su extensión de DSL principal a nivel del proyecto. Se encuentra dentro de la jerarquía de destinos de KMP para garantizar que solo se aplique al destino de Android específico definido en tu configuración multiplataforma.
Cómo controlar las compilaciones y los conjuntos de orígenes
A menudo, los complementos deben funcionar en un nivel más detallado que solo el objetivo,
específicamente en el nivel de compilación. KotlinMultiplatformAndroidLibraryTarget contiene instancias de KotlinMultiplatformAndroidCompilation (por ejemplo, main, hostTest y deviceTest). Cada compilación se asocia con conjuntos de orígenes de Kotlin. Los complementos pueden interactuar con estos para agregar fuentes, dependencias o configurar tareas de compilación.
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 compilaciones de prueba en complementos de convenciones
Cuando configures valores predeterminados para compilaciones de prueba (como targetSdk para
pruebas de instrumentación) en un complemento de convenciones, evita usar métodos de habilitación, como
withDeviceTest { } o withHostTest { }. Llamar a estos métodos de forma inmediata activa la creación de las variantes de prueba de Android y las compilaciones correspondientes para cada módulo que aplica el complemento de convención, lo que podría no ser adecuado. Tampoco se puede llamar a estos métodos por segunda vez en un módulo específico para definir mejor la configuración, ya que, si lo haces, se genera un error que indica que ya se creó la compilación.
En su lugar, usa un bloque configureEach reactivo en el contenedor de compilaciones.
Esto te permite proporcionar parámetros de configuración predeterminados que solo se aplican si un módulo habilita de forma explícita la compilación de prueba:
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) }
}
}
}
}
}
Este patrón garantiza que tu complemento de convención siga siendo diferido y permite que los módulos individuales llamen a withDeviceTest { } para habilitar y personalizar aún más sus pruebas sin entrar en conflicto con los valores predeterminados.
Interactúa con la API de Variant
Para las tareas que requieren una configuración de etapa tardía, acceso a artefactos (como manifiestos o código de bytes) o la capacidad de habilitar o inhabilitar componentes específicos, debes usar la API de Android Variant. En los proyectos de KMP, la extensión es de tipo KotlinMultiplatformAndroidComponentsExtension.
La extensión se registra a nivel del proyecto cuando se aplica el complemento para Android de KMP.
Usa beforeVariants para controlar la creación de variantes o sus componentes de prueba anidados (hostTests y deviceTests). Este es el lugar correcto para inhabilitar pruebas de manera programática o cambiar los valores de las propiedades de 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 }
}
}
}
}
Usa onVariants para acceder al objeto de variante final (KotlinMultiplatformAndroidVariant). Aquí es donde puedes inspeccionar las propiedades resueltas o registrar transformaciones en artefactos, como el manifiesto combinado o las clases de biblioteca.
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)
}
}
}
}
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Configura tu entorno
- Agrega un módulo de KMP a un proyecto
- Configura el complemento de la biblioteca de Android para Gradle para KMP