В этом руководстве объясняется, как обнаруживать целевой объект Android, взаимодействовать с ним и настраивать его в проекте Kotlin Multiplatform (KMP) из пользовательского плагина Gradle. Используйте эти API при написании плагинов соглашений для сборки или публикации плагинов Gradle для проектов KMP.
Как проверить, используется ли в проекте плагин Kotlin Multiplatform
Чтобы избежать ошибок и убедиться, что ваш плагин запускается только при наличии KMP, вы должны проверить, использует ли проект плагин KMP. Рекомендуется использовать plugins.withId(), чтобы реагировать на применение плагина KMP, а не проверять его сразу. Такой реактивный подход позволяет избежать проблем, связанных с порядком применения плагинов в скриптах сборки.
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.
}
}
}
Доступ к модели
Точкой входа для всех конфигураций Kotlin Multiplatform является расширение 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)
}
}
}
Реагирование на целевые платформы Kotlin Multiplatform
Используйте контейнер targets, чтобы настроить плагин для каждой цели, которую добавляет пользователь.
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
}
}
}
}
Применение логики, зависящей от цели
Если плагин должен применять логику только к определенным типам платформ, обычно проверяется свойство platformType. Это перечисление, которое в общих чертах классифицирует цель.
Например, используйте этот вариант, если плагину нужно только общее различие (например, запуск только на целевых объектах, подобных 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) */ }
}
}
}
}
}
Особенности для Android
Хотя все целевые платформы Android имеют индикатор platformType.androidJvm, у KMP есть две разные точки интеграции в зависимости от используемого плагина Android Gradle: KotlinAndroidTarget для проектов, использующих com.android.library или com.android.application, и KotlinMultiplatformAndroidLibraryTarget для проектов, использующих com.android.kotlin.multiplatform.library.
API KotlinMultiplatformAndroidLibraryTarget был добавлен в AGP 8.8.0, поэтому, если пользователи вашего плагина используют более раннюю версию AGP, проверка target is KotlinMultiplatformAndroidLibraryTarget может привести к ошибке ClassNotFoundException. Чтобы обеспечить безопасность, проверяйте AndroidPluginVersion.getCurrent() перед проверкой типа таргетинга.
Для AndroidPluginVersion.getCurrent() требуется AGP 8.3.1 или более поздней версии.
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
}
}
}
}
}
Доступ к расширению Android KMP и его свойствам
Ваш плагин в основном взаимодействует с расширением Kotlin, предоставляемым плагином Kotlin Multiplatform, и расширением Android, предоставляемым AGP для целевой платформы KMP Android. Блок android {} в расширении Kotlin в проекте KMP представлен интерфейсом KotlinMultiplatformAndroidLibraryTarget, который также расширяет KotlinMultiplatformAndroidLibraryExtension.
Это означает, что вы можете получить доступ к свойствам DSL, относящимся как к целевому объекту, так и к Android, через этот единственный объект.
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
}
}
}
}
В отличие от других плагинов Android (таких как com.android.library или com.android.application), плагин KMP Android не регистрирует свое основное расширение DSL на уровне проекта. Он находится в иерархии целей KMP, чтобы гарантировать, что он применяется только к конкретной цели Android, определенной в вашей мультиплатформенной настройке.
Как работать с компиляциями и наборами исходных файлов
Часто плагинам требуется работать на более детальном уровне, чем просто цель, а именно на уровне компиляции. KotlinMultiplatformAndroidLibraryTarget содержит экземпляры KotlinMultiplatformAndroidCompilation (например, main, hostTest, deviceTest). Каждая компиляция связана с наборами исходных файлов Kotlin. Плагины могут взаимодействовать с ними, чтобы добавлять источники, зависимости или настраивать задачи компиляции.
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 ->
}
}
}
}
}
}
Настройка тестовых компиляций в плагинах соглашений
При настройке значений по умолчанию для тестовых компиляций (например, targetSdk для инструментальных тестов) в плагине соглашения не используйте методы включения, такие как withDeviceTest { } или withHostTest { }. Нетерпеливый вызов этих методов запускает создание соответствующих тестовых вариантов Android и компиляций для каждого модуля, к которому применяется плагин соглашения, что может не подойти. Эти методы также нельзя вызвать повторно в определенном модуле, чтобы уточнить настройки, поскольку это приведет к ошибке, указывающей на то, что компиляция уже создана.
Вместо этого используйте реактивный блок configureEach в контейнере компиляций.
Это позволяет вам предоставлять конфигурации по умолчанию, которые применяются только в том случае, если модуль явно включает тестовую компиляцию:
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) }
}
}
}
}
}
Такой подход гарантирует, что плагин соглашения останется отложенным и позволит отдельным модулям вызывать withDeviceTest { }, чтобы включать и настраивать тесты, не конфликтуя с настройками по умолчанию.
Взаимодействие с Variant API
Для задач, требующих настройки на поздней стадии, доступа к артефактам (например, манифестам или байт-коду) или возможности включать или отключать определенные компоненты, необходимо использовать Android Variant API. В проектах KMP расширение имеет тип KotlinMultiplatformAndroidComponentsExtension.
Расширение регистрируется на уровне проекта при применении плагина KMP Android.
Используйте beforeVariants, чтобы управлять созданием вариантов или их вложенных тестовых компонентов (hostTests и deviceTests). Это подходящее место для программного отключения тестов или изменения значений свойств 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 }
}
}
}
}
Используйте onVariants, чтобы получить доступ к объекту окончательного варианта (KotlinMultiplatformAndroidVariant). Это позволит вам проверить разрешенные свойства или зарегистрировать преобразования в таких артефактах, как объединенный манифест или классы библиотек.
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)
}
}
}
}
Рекомендуем
- Примечание. Текст ссылки показывается, когда JavaScript отключен.
- Как настроить среду
- Как добавить модуль KMP в проект
- Как настроить плагин Android Gradle Library для KMP