เอกสารนี้เป็นคำแนะนำสำหรับผู้เขียนปลั๊กอินเกี่ยวกับวิธีตรวจหา
โต้ตอบ และกำหนดค่าการตั้งค่า 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