Generator implementacji rozwiązania Parcelable

kotlin-parcelizeWtyczka udostępnia generator implementacji Parcelable.

Aby uwzględnić obsługę Parcelable, dodaj wtyczkę Gradle do pliku build.gradle aplikacji:

Dynamiczny

plugins {
    id 'kotlin-parcelize'
}

Kotlin

plugins {
    id("kotlin-parcelize")
}

Gdy dodasz do klasy adnotację @Parcelize, automatycznie zostanie wygenerowana implementacja Parcelable, jak pokazano w tym przykładzie:

// import kotlinx.parcelize.Parcelize

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

@Parcelize wymaga, aby wszystkie serializowane właściwości były zadeklarowane w konstruktorze głównym. Wtyczka wyświetla ostrzeżenie dotyczące każdej właściwości, która ma pole zapasowe zadeklarowane w treści klasy. Nie możesz też zastosować @Parcelize, jeśli niektóre parametry konstruktora głównego nie są właściwościami.

Jeśli Twoja klasa wymaga bardziej zaawansowanej logiki serializacji, napisz ją w klasie towarzyszącej:

@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
        }
    }
}

Obsługiwane typy

@Parcelize obsługuje szeroką gamę typów:

  • Typy proste (i ich wersje opakowane)
  • Obiekty i wyliczenia
  • String, CharSequence
  • Duration
  • Exception
  • Size, SizeF, Bundle, IBinder, IInterface, FileDescriptor
  • SparseArray, SparseIntArray, SparseLongArray, SparseBooleanArray
  • Wszystkie implementacje Serializable (w tym Date) i Parcelable
  • Kolekcje wszystkich obsługiwanych typów: List (mapowane na ArrayList), Set (mapowane na LinkedHashSet), Map (mapowane na LinkedHashMap)
    • Istnieje też wiele konkretnych implementacji: ArrayList, LinkedList, SortedSet, NavigableSet, HashSet, LinkedHashSet, TreeSet, SortedMap, NavigableMap, HashMap, LinkedHashMap, TreeMap, ConcurrentHashMap
  • Tablice wszystkich obsługiwanych typów
  • Wersje dopuszczające wartość null wszystkich obsługiwanych typów

Niestandardowe Parceler

Jeśli Twój typ nie jest obsługiwany bezpośrednio, możesz utworzyć dla niego Parcelerobiekt mapowania.

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)
    }
}

Możesz zastosować zewnętrzne narzędzia do analizy składni za pomocą adnotacji @TypeParceler lub @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

Tworzenie danych z Parcel

W kodzie Java możesz uzyskać dostęp do pola CREATOR bezpośrednio.

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

W języku Kotlin nie możesz używać pola CREATOR bezpośrednio. Zamiast tego użyj zasady kotlinx.parcelize.parcelableCreator.

// import kotlinx.parcelize.parcelableCreator

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

Pomijanie właściwości podczas serializacji

Jeśli chcesz pominąć podział niektórych usług, użyj adnotacji @IgnoredOnParcel. Można jej też używać we właściwościach w treści klasy, aby wyciszyć ostrzeżenia o tym, że właściwość nie jest serializowana. Właściwości konstruktora oznaczone adnotacją @IgnoredOnParcel muszą mieć wartość domyślną.

@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
}

Używanie metody android.os.Parcel.writeValue do serializacji właściwości

Możesz dodać do typu adnotację @RawValue, aby Parcelize używał Parcel.writeValue w przypadku tej właściwości.

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

Może to spowodować błąd w czasie działania, jeśli wartość właściwości nie jest natywnie obsługiwana przez Androida.

Parcelize może też wymagać użycia tej adnotacji, gdy nie ma innego sposobu serializacji właściwości.

Parcelizacja za pomocą klas i interfejsów zamkniętych

Parcelize wymaga, aby klasa do spakowania nie była abstrakcyjna. To ograniczenie nie dotyczy klas zamkniętych. Jeśli adnotacja @Parcelize jest używana w przypadku klasy zamkniętej, nie trzeba jej powtarzać w przypadku klas pochodnych.

@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

Konfigurowanie Parcelize dla Kotlin Multiplatform

funkcje będą znacznie ograniczone.

Przed wprowadzeniem Kotlin 2.0 można było używać Parcelize, tworząc aliasy adnotacji Parcelize za pomocą expectactual:

// 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

W języku Kotlin w wersji 2.0 i nowszych aliasowanie adnotacji, które wywołują wtyczki, jest niedostępne. Aby to obejść, podaj nową adnotację Parcelize jako parametr additionalAnnotation wtyczki.

// 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

Interfejs Parcel jest dostępny tylko na Androidzie, więc Parcelize nie będzie generować kodu na innych platformach. Implementacje actual mogą być tam puste. Nie można też używać w kodzie wspólnym żadnych adnotacji, które wymagają odwoływania się do klasy Parcel, np. @WriteWith.

Funkcje eksperymentalne

Serializator klasy danych

Dostępne od wersji Kotlin 2.1.0.

Adnotacja DataClass umożliwia serializację klas danych tak, jakby były one same oznaczone adnotacją Parcelize. Ta adnotacja wymaga wyrażenia zgody na 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

Konstruktor podstawowy i wszystkie jego właściwości muszą być dostępne z klasy Parcelable. Dodatkowo wszystkie właściwości głównego konstruktora klasy danych muszą być obsługiwane przez Parcelize. Jeśli wybrano opcję Custom Parcelers, należy ją określić w klasie Parcelable, a nie w klasie danych. Jeśli klasa danych implementuje jednocześnie Serializable, adnotacja @DataClass jest ważniejsza: android.os.Parcel.writeSerializable nie będzie używana.

Praktycznym zastosowaniem tego rozwiązania jest serializacja kotlin.Pair. Innym przydatnym przykładem jest uproszczenie kodu wieloplatformowego: kod wspólny może deklarować warstwę danych jako klasy danych, które kod Androida może następnie rozszerzyć o logikę serializacji, eliminując potrzebę stosowania w kodzie wspólnym adnotacji i aliasów typów specyficznych dla Androida.

// 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

Parametry inne niż val lub var w konstruktorze podstawowym

Dostępne od wersji Kotlin 2.1.0.

Aby włączyć tę funkcję, dodaj experimentalCodeGeneration=true do argumentów wtyczki parcelize.

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

Ta funkcja znosi ograniczenie dotyczące argumentów konstruktora głównego, które muszą być typu val lub var. Rozwiązuje to jeden z problemów związanych z używaniem parcelize z dziedziczeniem, który wcześniej wymagał użycia właściwości 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)

Takie parametry mogą być używane tylko w argumentach konstruktora klasy bazowej. Nie można się do nich odwoływać w treści zajęć.

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

Prześlij opinię

Jeśli napotkasz problemy z kotlin-parcelizewtyczką Gradle, możesz zgłosić błąd.