يقدّم هذا المستند دليلًا لمؤلفي المكوّنات الإضافية حول كيفية رصد إعدادات Kotlin Multiplatform (KMP) والتفاعل معها وضبطها بشكل صحيح، مع التركيز بشكل خاص على التكامل مع استهدافات Android ضِمن مشروع KMP. مع استمرار تطوّر KMP، من الضروري فهم أدوات الربط وواجهات برمجة التطبيقات المناسبة، مثل أنواع 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. هذه قيمة تعدادية تصنّف الهدف بشكل عام.
على سبيل المثال، استخدِم هذا الخيار إذا كانت الإضافة تحتاج فقط إلى التمييز بشكل عام (على سبيل المثال، التشغيل على أهداف شبيهة بآلة Java الافتراضية فقط):
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 نقطتَي تكامل مختلفتَين حسب إصدار إضافة Android Gradle المستخدَمة: KotlinAndroidTarget للمشاريع التي تستخدم com.android.library أو com.android.application، وKotlinMultiplatformAndroidLibraryTarget للمشاريع التي تستخدم com.android.kotlin.multiplatform.library.
تمت إضافة واجهة برمجة التطبيقات KotlinMultiplatformAndroidLibraryTarget في الإصدار 8.8.0 من "مكوّن Android الإضافي"، لذا إذا كان مستخدمو المكوّن الإضافي يستخدمون إصدارًا أقدم من "مكوّن Android الإضافي"، قد يؤدي التحقّق من target is KotlinMultiplatformAndroidLibraryTarget إلى حدوث ClassNotFoundException. لضمان أمان ذلك، تحقَّق من
AndroidPluginVersion.getCurrent() قبل التحقّق من نوع الهدف.
يُرجى العِلم أنّ AndroidPluginVersion.getCurrent() يتطلّب الإصدار 7.1 من "مكوّن Android الإضافي لبرنامج Gradle" أو إصدارًا أحدث.
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 التي يوفّرها "مكوّن Android الإضافي في Gradle" (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)، لا يسجّل مكوّن Android الإضافي في KMP إضافة 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 {
targetSdk { version = release(34) }
}
}
}
}
}
يضمن هذا النمط بقاء إضافة الاتفاقية غير نشطة إلى أن يتم استدعاؤها، كما يتيح للوحدات الفردية استدعاء withDeviceTest { } لتفعيل اختباراتها وتخصيصها بشكل أكبر بدون التعارض مع الإعدادات التلقائية.
التفاعل مع Variant API
بالنسبة إلى المهام التي تتطلّب إعدادات في مرحلة متأخرة أو الوصول إلى العناصر (مثل ملفات البيان أو رمز البايت) أو إمكانية تفعيل أو إيقاف مكوّنات معيّنة، عليك استخدام Android Variant API. في مشاريع KMP، يكون نوع الإضافة KotlinMultiplatformAndroidComponentsExtension.
يتم تسجيل الإضافة على مستوى المشروع عند تطبيق إضافة Android في KMP.
استخدِم 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.variant.SingleArtifact.MERGED_MANIFEST)
}
}
}
}
الإجراءات المقترَحة
- ملاحظة: يتم عرض نص الرابط عندما تكون JavaScript غير مفعّلة.
- إعداد البيئة
- إضافة وحدة KMP إلى مشروع
- إعداد المكوّن الإضافي Android Gradle Library لنظام KMP