Z tego przewodnika dowiesz się, jak wykrywać, konfigurować i używać środowiska docelowego Androida w projekcie Kotlin Multiplatform (KMP) z niestandardowej wtyczki do obsługi Gradle. Używaj tych interfejsów API podczas pisania wtyczek konwencji do kompilacji lub publikowania wtyczek Gradle do projektów KMP.
Sprawdzanie, czy projekt używa wtyczki Kotlin Multiplatform
Aby uniknąć błędów i mieć pewność, że wtyczka działa tylko wtedy, gdy KMP jest obecny, musisz sprawdzić, czy projekt używa wtyczki KMP. Sprawdzoną metodą jest użycie plugins.withId() do reagowania na zastosowanie wtyczki KMP, zamiast natychmiastowego sprawdzania, czy została zastosowana. Takie reaktywne podejście zapobiega niestabilności wtyczki w zależności od kolejności, w jakiej wtyczki są stosowane w skryptach kompilacji użytkownika.
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.
}
}
}
Uzyskiwanie dostępu do modelu
Punktem wejścia dla wszystkich konfiguracji Kotlin Multiplatform jest rozszerzenie 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)
}
}
}
Reagowanie na cele Kotlin Multiplatform
Użyj kontenera targets, aby reaktywnie konfigurować wtyczkę dla każdego celu dodanego przez użytkownika.
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
}
}
}
}
Zastosuj logikę związaną z celami
Jeśli wtyczka musi stosować logikę tylko do określonych typów platform, typowym podejściem jest sprawdzenie właściwości platformType. Jest to typ wyliczeniowy, który ogólnie kategoryzuje cel.
Możesz na przykład użyć tej opcji, jeśli wtyczka musi tylko rozróżniać ogólne środowiska (np. działać tylko na platformach podobnych do 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) */ }
}
}
}
}
}
Szczegóły dotyczące Androida
Wszystkie cele Androida mają wskaźnik platformType.androidJvm, ale KMP ma 2 różne punkty integracji w zależności od używanej wtyczki Androida do obsługi Gradle: KotlinAndroidTarget w przypadku projektów korzystających z com.android.library lub com.android.application oraz KotlinMultiplatformAndroidLibraryTarget w przypadku projektów korzystających z com.android.kotlin.multiplatform.library.
Interfejs API KotlinMultiplatformAndroidLibraryTarget został dodany w AGP 8.8.0, więc jeśli użytkownicy Twojej wtyczki korzystają z niższej wersji AGP, sprawdzenie target is KotlinMultiplatformAndroidLibraryTarget może spowodować błąd ClassNotFoundException. Aby było to bezpieczne, przed sprawdzeniem typu celu sprawdź AndroidPluginVersion.getCurrent().
AndroidPluginVersion.getCurrent() wymaga AGP w wersji 8.3.1 lub nowszej.
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
}
}
}
}
}
Uzyskiwanie dostępu do rozszerzenia Android KMP i jego właściwości
Wtyczka wchodzi w interakcję głównie z rozszerzeniem Kotlin udostępnianym przez wtyczkę Kotlin Multiplatform i rozszerzeniem Androida udostępnianym przez AGP dla celu KMP na Androida. Blok android {} w rozszerzeniu Kotlin w projekcie KMP jest reprezentowany przez interfejs KotlinMultiplatformAndroidLibraryTarget, który rozszerza też KotlinMultiplatformAndroidLibraryExtension.
Oznacza to, że za pomocą tego jednego obiektu możesz uzyskać dostęp zarówno do właściwości DSL związanych z platformą docelową, jak i z Androidem.
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
}
}
}
}
W przeciwieństwie do innych wtyczek Androida (takich jak com.android.library lub com.android.application) wtyczka KMP Android nie rejestruje głównego rozszerzenia DSL na poziomie projektu. Znajduje się w hierarchii docelowej KMP, aby mieć pewność, że dotyczy tylko konkretnego celu Androida zdefiniowanego w konfiguracji wieloplatformowej.
Obsługa kompilacji i zbiorów źródeł
Często wtyczki muszą działać na bardziej szczegółowym poziomie niż tylko na poziomie celu, a konkretnie na poziomie kompilacji. KotlinMultiplatformAndroidLibraryTarget zawiera instancje KotlinMultiplatformAndroidCompilation (np. main, hostTest, deviceTest). Każda kompilacja jest powiązana ze zbiorami źródeł Kotlina. Wtyczki mogą wchodzić z nimi w interakcje, aby dodawać źródła, zależności lub konfigurować zadania kompilacji.
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 ->
}
}
}
}
}
}
Konfigurowanie kompilacji testowych we wtyczkach konwencji
Podczas konfigurowania wartości domyślnych kompilacji testowych (takich jak targetSdk w przypadku testów z instrumentacją) we wtyczce konwencji unikaj używania metod aktywujących, takich jak withDeviceTest { } lub withHostTest { }. Wywołanie tych metod powoduje utworzenie odpowiednich wariantów testowych Androida i kompilacji dla każdego modułu, który stosuje wtyczkę konwencji, co może nie być odpowiednie. Nie można też wywołać tych metod po raz drugi w określonym module, aby doprecyzować ustawienia, ponieważ spowoduje to błąd informujący, że kompilacja została już utworzona.
Zamiast tego użyj reaktywnego bloku configureEach w kontenerze kompilacji.
Dzięki temu możesz podać domyślne konfiguracje, które mają zastosowanie tylko wtedy, gdy moduł wyraźnie włącza kompilację testową:
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) }
}
}
}
}
}
Ten wzorzec sprawia, że wtyczka konwencji pozostaje leniwa i umożliwia poszczególnym modułom wywoływanie funkcji withDeviceTest { } w celu włączenia i dalszego dostosowywania testów bez konfliktów z ustawieniami domyślnymi.
Interakcja z interfejsem Variant API
W przypadku zadań, które wymagają konfiguracji na późnym etapie, dostępu do artefaktów (takich jak pliki manifestu lub kod bajtowy) albo możliwości włączania lub wyłączania określonych komponentów, musisz użyć interfejsu Android Variant API. W projektach KMP rozszerzenie jest typu KotlinMultiplatformAndroidComponentsExtension.
Rozszerzenie jest rejestrowane na poziomie projektu, gdy stosowana jest wtyczka Androida KMP.
Użyj beforeVariants, aby kontrolować tworzenie wariantów lub ich zagnieżdżonych komponentów testowych (hostTests i deviceTests). To właściwe miejsce do automatycznego wyłączania testów lub zmieniania wartości właściwości 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 }
}
}
}
}
Użyj onVariants, aby uzyskać dostęp do obiektu ostatecznego wariantu (KotlinMultiplatformAndroidVariant). Tutaj możesz sprawdzać rozwiązane właściwości lub rejestrować przekształcenia artefaktów, takich jak manifest łączony czy klasy biblioteki.
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)
}
}
}
}
Polecane dla Ciebie
- Uwaga: tekst linku jest wyświetlany, gdy język JavaScript jest wyłączony.
- Konfigurowanie środowiska
- Dodawanie modułu KMP do projektu
- Konfigurowanie wtyczki biblioteki Androida do obsługi Gradle na potrzeby KMP