คู่มือนี้อธิบายวิธีตรวจจับ โต้ตอบ และกำหนดค่าเป้าหมาย 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 นี่คือ enum ที่จัดหมวดหมู่เป้าหมายอย่างกว้างๆ
ตัวอย่างเช่น ใช้ตัวเลือกนี้หากปลั๊กอินของคุณต้องการแยกความแตกต่างในวงกว้างเท่านั้น (เช่น ทำงานบนเป้าหมายที่คล้ายกับ 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 มีจุดผสานรวมที่แตกต่างกัน 2 จุด โดยขึ้นอยู่กับปลั๊กอิน Android Gradle ที่ใช้ ได้แก่ KotlinAndroidTarget สำหรับโปรเจ็กต์ที่ใช้ com.android.library หรือ com.android.application และ KotlinMultiplatformAndroidLibraryTarget สำหรับโปรเจ็กต์ที่ใช้ com.android.kotlin.multiplatform.library
เพิ่ม KotlinMultiplatformAndroidLibraryTarget API ใน 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
}
}
}
}
}
เข้าถึงส่วนขยาย KMP ของ Android และพร็อพเพอร์ตี้ของส่วนขยาย
ปลั๊กอินของคุณจะโต้ตอบกับส่วนขยาย Kotlin ที่ปลั๊กอิน Kotlin Multiplatform มีให้เป็นหลัก และส่วนขยาย Android ที่ AGP มีให้สำหรับเป้าหมาย Android ของ KMP บล็อก 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
}
}
}
}
ปลั๊กอิน KMP Android ไม่ได้ลงทะเบียนส่วนขยาย DSL หลักที่ระดับโปรเจ็กต์ ซึ่งแตกต่างจากปลั๊กอิน Android อื่นๆ (เช่น com.android.library หรือ com.android.application) โดยจะอยู่ในลำดับชั้นเป้าหมาย KMP เพื่อให้แน่ใจว่าจะมีผลกับเป้าหมาย Android ที่เฉพาะเจาะจงซึ่งกำหนดไว้ในการตั้งค่าหลายแพลตฟอร์มเท่านั้น
จัดการการคอมไพล์และชุดซอร์ส
บ่อยครั้งที่ปลั๊กอินต้องทำงานในระดับที่ละเอียดยิ่งขึ้นกว่าแค่เป้าหมาย โดยเฉพาะในระดับการคอมไพล์ KotlinMultiplatformAndroidLibraryTarget มีอินสแตนซ์ KotlinMultiplatformAndroidCompilation (เช่น main, hostTest, deviceTest) การคอมไพล์แต่ละรายการจะเชื่อมโยงกับชุดซอร์ส Kotlin ปลั๊กอินสามารถโต้ตอบกับสิ่งเหล่านี้เพื่อเพิ่มแหล่งที่มา ทรัพยากร Dependency หรือกำหนดค่างานการคอมไพล์
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 ที่เกี่ยวข้องและการคอมไพล์สำหรับทุกโมดูลที่ใช้ปลั๊กอินแบบแผน ซึ่งอาจไม่เหมาะสม นอกจากนี้ คุณยังเรียกใช้เมธอดเหล่านี้เป็นครั้งที่ 2 ในโมดูลที่เฉพาะเจาะจงเพื่อปรับแต่งการตั้งค่าไม่ได้ เนื่องจากจะทำให้เกิดข้อผิดพลาดที่ระบุว่ามีการสร้างการคอมไพล์แล้ว
แต่ให้ใช้บล็อก 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) }
}
}
}
}
}
รูปแบบนี้ช่วยให้มั่นใจได้ว่าปลั๊กอินแบบแผนจะยังคงเป็นแบบ Lazy และอนุญาตให้แต่ละโมดูลเรียกใช้ withDeviceTest { } เพื่อเปิดใช้และปรับแต่งการทดสอบเพิ่มเติมโดยไม่ขัดแย้งกับค่าเริ่มต้น
โต้ตอบกับ Variant API
สำหรับงานที่ต้องมีการกำหนดค่าในระยะหลัง การเข้าถึงอาร์ติแฟกต์ (เช่น ไฟล์ Manifest หรือไบต์โค้ด) หรือความสามารถในการเปิดหรือปิดคอมโพเนนต์ที่เฉพาะเจาะจง คุณต้องใช้ 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) ซึ่งเป็นที่ที่คุณสามารถตรวจสอบพร็อพเพอร์ตี้ที่แก้ไขแล้วหรือลงทะเบียนการเปลี่ยนรูปแบบในอาร์ติแฟกต์ เช่น ไฟล์ Manifest ที่ผสานแล้วหรือคลาสไลบรารี
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 สำหรับ KMP