منشئ تنفيذ Parcelable

يوفّر المكوّن الإضافي kotlin-parcelize أداة إنشاء ملف تنفيذ Parcelable.

لتضمين ميزة التوافق مع Parcelable، أضِف المكوّن الإضافي Gradle إلىملفbuild.gradle في تطبيقك:

رائع

plugins {
    id 'kotlin-parcelize'
}

Kotlin

plugins {
    id("kotlin-parcelize")
}

عند إضافة تعليقات توضيحية إلى فئة باستخدام @Parcelize، يتم إنشاء عملية تنفيذ Parcelable تلقائيًا، كما هو موضّح في المثال التالي:

import kotlinx.parcelize.Parcelize

@Parcelize
class User(val firstName: String, val lastName: String, val age: Int): Parcelable

تتطلّب @Parcelize أن يتمّ الإعلان عن جميع السمات التسلسلية في المنشئ الأساسي. يُصدر المكون الإضافي تحذيرًا لكل موقع مع وجود حقل خلفي محدد في نص الفئة. ولا يمكنك أيضًا تطبيق @Parcelize إذا كانت بعض مَعلمات الدالة الإنشائية الأساسية ليست سمات.

إذا كانت صفتك تتطلّب منطقًا أكثر تقدمًا لتحويل البيانات إلى سلسلة، اكتب هذا المنطق داخل صف مصاحب:

@Parcelize
data class User(val firstName: String, val lastName: String, val age: Int) : Parcelable {
    private companion object : Parceler<User> {
        override fun User.write(parcel: Parcel, flags: Int) {
            // Custom write implementation
        }

        override fun create(parcel: Parcel): User {
            // Custom read implementation
        }
    }
}

الأنواع المتوافقة

تتيح @Parcelize مجموعة كبيرة من الأنواع:

  • الأنواع الأساسية (ونُسخها المُعبأة)
  • الكائنات والقوائم المحدَّدة
  • String، CharSequence
  • Duration
  • Exception
  • Size، SizeF، Bundle، IBinder، IInterface، FileDescriptor
  • SparseArray، SparseIntArray، SparseLongArray، SparseBooleanArray
  • جميع عمليات تنفيذ Serializable (بما في ذلك Date) وParcelable
  • مجموعات من جميع الأنواع المتوافقة: List (تم ربطها بـ ArrayList)، وSet (تم ربطها بـ LinkedHashSet)، وMap (تم ربطها بـ LinkedHashMap)
    • بالإضافة إلى عدد من عمليات التنفيذ الملموسة: ArrayList وLinkedList SortedSet وNavigableSet وHashSet وLinkedHashSet وTreeSet SortedMap وNavigableMap وHashMap وLinkedHashMap وTreeMap ConcurrentHashMap
  • صفائف من جميع الأنواع المتوافقة
  • الإصدارات الفارغة من كل الأنواع المتوافقة

Parceler المخصّصة

إذا لم يكن نوعك متوافقًا مباشرةً، يمكنك كتابة Parceler عنصر تعيين له.

class ExternalClass(val value: Int)

object ExternalClassParceler : Parceler<ExternalClass> {
    override fun create(parcel: Parcel) = ExternalClass(parcel.readInt())

    override fun ExternalClass.write(parcel: Parcel, flags: Int) {
        parcel.writeInt(value)
    }
}

يمكنك تطبيق الطرود الخارجية باستخدام تعليقات @TypeParceler أو @WriteWith:

// Class-local parceler
@Parcelize
@TypeParceler<ExternalClass, ExternalClassParceler>()
class MyClass(val external: ExternalClass) : Parcelable

// Property-local parceler
@Parcelize
class MyClass(@TypeParceler<ExternalClass, ExternalClassParceler>() val external: ExternalClass) : Parcelable

// Type-local parceler
@Parcelize
class MyClass(val external: @WriteWith<ExternalClassParceler>() ExternalClass) : Parcelable

إنشاء بيانات من Parcel

في رمز Java، يمكنك الوصول إلى الحقل CREATOR مباشرةً.

class UserCreator {
    static User fromParcel(Parcel parcel) {
        return User.CREATOR.createFromParcel(parcel);
    }
}

في Kotlin، لا يمكنك استخدام الحقل CREATOR مباشرةً. واستخدِم kotlinx.parcelize.parcelableCreator بدلاً من ذلك.

import kotlinx.parcelize.parcelableCreator

fun userFromParcel(parcel: Parcel): User {
    return parcelableCreator<User>().createFromParcel(parcel)
}

تخطّي المواقع من التسلسل

إذا أردت عدم تقسيم بعض المواقع، استخدِم التعليق التوضيحي @IgnoredOnParcel. ويمكن استخدامه أيضًا على السمات ضمن محتوى الفئة لإيقاف التحذيرات بشأن عدم تسلسل السمة. يجب أن تحتوي سمات المنشئ التي تحتوي على تعليقات توضيحية باستخدام @IgnoredOnParcel على قيمة تلقائية.

@Parcelize
class MyClass(
    val include: String,
    // Don't serialize this property
    @IgnoredOnParcel val ignore: String = "default"
): Parcelable {
    // Silence a warning
    @IgnoredOnParcel
    val computed: String = include + ignore
}

استخدام android.os.Parcel.writeValue لتسلسل خاصية

يمكنك إضافة تعليق توضيحي لنوع باستخدام @RawValue لجعل Parcelize يستخدم Parcel.writeValue لهذا السمة.

@Parcelize
class MyClass(val external: @RawValue ExternalClass): Parcelable

قد يتعذّر تنفيذ هذا الإجراء في وقت التشغيل إذا لم تكن قيمة السمة متوافقة مع Android بشكلٍ أصلي.

قد تتطلّب منك أداة Parcelize أيضًا استخدام هذا التعليق التوضيحي في حال عدم توفّر طريقة أخرى لتسلسل السمة.

تقسيم التطبيق إلى أجزاء باستخدام فئات وموصّلات مغلقة

تتطلّب ميزة Parcelize أن تكون الفئة التي يتم تقسيمها غير مجردة. لا ينطبق هذا الحدّ على الصفوف المُغلقة. عند استخدام التعليق التوضيحي @Parcelize على صف مغلق، لا يلزم تكراره للفئات المشتقة.

@Parcelize
sealed class SealedClass: Parcelable {
    class A(val a: String): SealedClass()
    class B(val b: Int): SealedClass()
}

@Parcelize
class MyClass(val a: SealedClass.A, val b: SealedClass.B, val c: SealedClass): Parcelable

إعداد Parcelize للأنظمة الأساسية المتعددة بلغة Kotlin

قبل Kotlin 2.0، كان بإمكانك استخدام Parcelize من خلال إنشاء أسماء بديلة للتعليقات التوضيحية في Parcelize باستخدام expect وactual:

// Common code
package example

@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
expect annotation class MyParcelize()

expect interface MyParcelable

@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.SOURCE)
expect annotation class MyIgnoredOnParcel()

@MyParcelize
class MyClass(
    val x: String,
    @MyIgnoredOnParcel val y: String = ""
): MyParcelable

// Platform code
package example

actual typealias MyParcelize = kotlinx.parcelize.Parcelize
actual typealias MyParcelable = android.os.Parcelable
actual typealias MyIgnoredOnParcel = kotlinx.parcelize.IgnoredOnParcel

في الإصدار 2.0 من Kotlin والإصدارات الأحدث، لا يُسمَح باستخدام التعليقات التوضيحية التي تؤدي إلى تنشيط المكوّنات الإضافية. لحلّ هذه المشكلة، قدِّم تعليقًا توضيحيًا جديدًا Parcelize كمَعلمة additionalAnnotation في المكوّن الإضافي بدلاً من ذلك.

// Gradle build configuration
kotlin {
    androidTarget {
        compilerOptions {
            // ...
            freeCompilerArgs.addAll("-P", "plugin:org.jetbrains.kotlin.parcelize:additionalAnnotation=example.MyParcelize")
        }
    }
}
// Common code
package example

@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
// No `expect` keyword here
annotation class MyParcelize()

expect interface MyParcelable

@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.SOURCE)
expect annotation class MyIgnoredOnParcel()

@MyParcelize
class MyClass(
    val x: String,
    @MyIgnoredOnParcel val y: String = ""
): MyParcelable

// Platform code
package example

// No typealias for MyParcelize here
actual typealias MyParcelable = android.os.Parcelable
actual typealias MyIgnoredOnParcel = kotlinx.parcelize.IgnoredOnParcel

بما أنّ واجهة Parcel متوفرة فقط على أجهزة Android، لن تنشئ Parcelize أي رمز على الأنظمة الأساسية الأخرى، لذلك قد تكون أي عمليات تنفيذ لـ actual فارغة. ولا يمكن أيضًا استخدام أي تعليق توضيحي يتطلّب الإشارة إلى الفئة Parcel، على سبيل المثال @WriteWith، في الرموز الشائعة.

ميزات تجريبية

معرِّف فئة البيانات

متوفّرة منذ Kotlin 2.1.0.

يسمح التعليق التوضيحي DataClass بتسلسل فئات البيانات كما لو كانت نفسها مُشارَكًا فيها باستخدام Parcelize. يتطلّب هذا التعليق التوضيحي تفعيل ميزة kotlinx.parcelize.Experimental.

@file:OptIn(kotlinx.parcelize.Experimental::class)

data class C(val a: Int, val b: String)

@Parcelize
class P(val c: @DataClass C) : Parcelable

يجب أن يكون بالإمكان الوصول إلى طريقة وضع التصميم الأساسية وجميع سماتها من فئة Parcelable. بالإضافة إلى ذلك، يجب أن تكون جميع سمات المنشئ الأساسية لفئة البيانات متوافقة مع Parcelize. يجب تحديد مقدّمي الطرود، في حال اختيارهم، في الفئة Parcelable، وليس فئة البيانات. إذا كانت فئة البيانات تنفّذ Serializable في الوقت نفسه، تكون الأولوية للتعليق التوضيحي @DataClass : لن يتم استخدامandroid.os.Parcel.writeSerializable.

ومن حالات الاستخدام العملية لهذا الإجراء هي تسلسل kotlin.Pair. ومن الأمثلة المفيدة الأخرى تبسيط الرمز البرمجي المتوافق مع أنظمة التشغيل المتعددة: يمكن للرمز البرمجي الشائع أن يُعلن عن طبقة البيانات كفئات بيانات، ويمكن لرمز Android بعد ذلك إضافة منطق التسلسل، ما يزيل الحاجة إلى التعليقات التوضيحية الخاصة بنظام التشغيل Android والأسماء البديلة للأنواع في الرمز البرمجي الشائع.

// Common code:
data class MyData(val x: String, val y: MoreData)
data class MoreData(val a: String, val b: Int)

// Platform code:
@OptIn(kotlinx.parcelize.Experimental::class)
@Parcelize
class DataWrapper(val wrapped: @DataClass MyData): Parcelable

استخدام مَعلمات غير val أو var في الدالة الانشائية الأساسية

متوفّرة منذ Kotlin 2.1.0.

لتفعيل هذه الميزة، أضِف experimentalCodeGeneration=true إلى وسيطات المكوّن الإضافي المقسّمة.

kotlin {
    compilerOptions {
        // ...
        freeCompilerArgs.addAll("-P", "plugin:org.jetbrains.kotlin.parcelize:experimentalCodeGeneration=true")
    }
}

تزيل هذه الميزة القيود المفروضة على وسيطات الدالة الإنشائية الأساسية التي يجب أن تكون val أو var. يحلّ هذا الأمر إحدى المشاكل المتعلقة باستخدام ميزة "تقسيم المنتج" مع الميزة "الاستبدال الوراثي"، التي كانت تتطلّب في السابق استخدام سمات open.

// base parcelize
@Parcelize
open class Base(open val s: String): Parcelable

@Parcelize
class Derived(
    val x: Int,
    // all arguments have to be `val` or `var` so we need to override
    // to not introduce new property name
    override val s: String
): Base(s)

// experimental code generation enabled
@Parcelize
open class Base(val s: String): Parcelable

@Parcelize
class Derived(val x: Int, s: String): Base(s)

ولا يُسمح باستخدام هذه المعلمات إلا في الوسيطات في الدالة الإنشائية للفئة الأساسية. ولا يُسمح بالإشارة إليها في نص الصف.

@Parcelize
class Derived(s: String): Base(s) { // allowed
    @IgnoredOnParcel
    val x: String = s // ERROR: not allowed.
    init {
        println(s) // ERROR: not allowed
    }
}

ملاحظات

إذا واجهت أي مشاكل في kotlin-parcelize المكوّن الإضافي Gradle، يمكنك إبلاغنا بالخطأ.