منشئ تنفيذ 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
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 إلى مَعلمات الإضافة parcelize.

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، يمكنك إبلاغنا بالخطأ.