เอกสารนี้เป็นคำแนะนำสำหรับผู้เขียนปลั๊กอินเกี่ยวกับวิธีตรวจหา
โต้ตอบ และกำหนดค่าการตั้งค่า Kotlin Multiplatform (KMP) อย่างถูกต้อง โดยเน้นที่การผสานรวมกับเป้าหมาย Android ภายในโปรเจ็กต์ KMP โดยเฉพาะ
คำแนะนำเหล่านี้มีผลไม่ว่าคุณจะสร้างปลั๊กอินตามธรรมเนียมเพื่อกำหนดค่าให้เป็นมาตรฐานในโมดูลของโปรเจ็กต์ หรือพัฒนาปลั๊กอินเพื่อใช้ในชุมชนในวงกว้าง เมื่อ KMP พัฒนาต่อไป การทำความเข้าใจฮุกและ API ที่เหมาะสม เช่น ประเภท KotlinMultiplatformExtension KotlinTarget และอินเทอร์เฟซการผสานรวมเฉพาะ Android เป็นสิ่งจำเป็นสำหรับการสร้างเครื่องมือที่แข็งแกร่งและพร้อมรับอนาคตซึ่งทำงานได้อย่างราบรื่นในทุกแพลตฟอร์มที่กำหนดไว้ในโปรเจ็กต์แบบหลายแพลตฟอร์ม
ตรวจสอบว่าโปรเจ็กต์ใช้ปลั๊กอิน 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 or Android */ }
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 7.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.mpp.KotlinAndroidTarget
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 ->
if (target is KotlinAndroidTarget) {
// Old kmp android integration using com.android.library or com.android.application
}
if (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 ต่างจากปลั๊กอิน Android อื่นๆ (เช่น com.android.library หรือ
com.android.application) ตรงที่จะไม่ลงทะเบียนส่วนขยาย 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 ->
}
}
}
}
}
}
กำหนดค่าการคอมไพล์การทดสอบในปลั๊กอินของ Convention
เมื่อกำหนดค่าเริ่มต้นสำหรับการคอมไพล์การทดสอบ (เช่น targetSdk สำหรับ
การทดสอบที่มีการตรวจสอบ) ในปลั๊กอินของรูปแบบ คุณควรหลีกเลี่ยงการใช้เมธอดที่เปิดใช้
เช่น withDeviceTest { } หรือ withHostTest { } การเรียกใช้เมธอดเหล่านี้
จะทริกเกอร์การสร้างตัวแปรทดสอบ Android ที่เกี่ยวข้องและการ
คอมไพล์สำหรับทุกโมดูลที่ใช้ปลั๊กอิน Convention ซึ่งอาจ
ไม่เหมาะสม นอกจากนี้ คุณจะเรียกใช้เมธอดเหล่านี้เป็นครั้งที่ 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 {
targetSdk { version = release(34) }
}
}
}
}
}
รูปแบบนี้ช่วยให้มั่นใจว่าปลั๊กอินของ Convention จะยังคงทำงานแบบ 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.variant.SingleArtifact.MERGED_MANIFEST)
}
}
}
}
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- ตั้งค่าสภาพแวดล้อม
- เพิ่มโมดูล KMP ลงในโปรเจ็กต์
- ตั้งค่าปลั๊กอินไลบรารี Android Gradle สำหรับ KMP