مولد پیاده سازی قابل حمل

افزونه kotlin-parcelize یک مولد پیاده سازی Parcelable را ارائه می دهد.

برای پشتیبانی از Parcelable ، افزونه Gradle را به فایل build.gradle برنامه خود اضافه کنید:

شیار

plugins {
    id 'kotlin-parcelize'
}

کاتلین

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 (نگاشت به ArrayListSet (نگاشت به LinkedHashSetMap (نگاشت به 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

در کد جاوا می توانید مستقیماً به فیلد CREATOR دسترسی داشته باشید.

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

در کاتلین نمی توانید مستقیماً از فیلد 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

در Kotlin 2.0 و بالاتر، نامگذاری حاشیه نویسی هایی که پلاگین ها را راه اندازی می کنند پشتیبانی نمی شود. برای دور زدن این موضوع، به جای آن یک حاشیه نویسی جدید 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 فقط در اندروید در دسترس است، 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 است. مثال مفید دیگر ساده‌سازی کد چند پلتفرمی است: کدهای رایج می‌توانند لایه داده را به عنوان کلاس‌های داده معرفی کنند، که کد اندروید می‌تواند آن را با منطق سریال‌سازی تقویت کند و نیاز به حاشیه‌نویسی خاص اندروید و نام مستعار در کدهای رایج را از بین ببرد.

// 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 باشند، برطرف می کند. این یک نقطه درد استفاده از parcelize با ارث را حل می کند، که قبلاً به استفاده از ویژگی های 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 با مشکلی مواجه شدید، می توانید یک اشکال را ثبت کنید .