Generator für Parcelable-Implementierungen

Das kotlin-parcelize-Plug-in bietet einen Parcelable-Implementierungsgenerator.

Wenn Sie Parcelable unterstützen möchten, fügen Sie der Datei build.gradle Ihrer App das Gradle-Plug-in hinzu:

Groovy

plugins {
    id 'kotlin-parcelize'
}

Kotlin

plugins {
    id("kotlin-parcelize")
}

Wenn Sie eine Klasse mit @Parcelize annotieren, wird automatisch eine Parcelable-Implementierung generiert, wie im folgenden Beispiel gezeigt:

import kotlinx.parcelize.Parcelize

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

Für @Parcelize müssen alle serialisierten Properties im primären Konstruktor deklariert werden. Das Plug-in gibt für jede Property mit einem im Klassenkörper deklarierten Back-End-Feld eine Warnung aus. Außerdem können Sie @Parcelize nicht anwenden, wenn einige der primären Konstruktorparameter keine Eigenschaften sind.

Wenn Ihre Klasse eine erweiterte Serialisierungslogik benötigt, schreiben Sie sie in eine Companion-Klasse:

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

Unterstützte Typen

@Parcelize unterstützt eine Vielzahl von Typen:

  • Einfache Typen (und ihre Boxed-Versionen)
  • Objekte und enums
  • String, CharSequence
  • Duration
  • Exception
  • Size, SizeF, Bundle, IBinder, IInterface, FileDescriptor
  • SparseArray, SparseIntArray, SparseLongArray, SparseBooleanArray
  • Alle Serializable- (einschließlich Date-) und Parcelable-Implementierungen
  • Sammlungen aller unterstützten Typen: List (auf ArrayList zugeordnet), Set (auf LinkedHashSet zugeordnet), Map (auf LinkedHashMap zugeordnet)
    • Einige konkrete Implementierungen: ArrayList, LinkedList, SortedSet, NavigableSet, HashSet, LinkedHashSet, TreeSet, SortedMap, NavigableMap, HashMap, LinkedHashMap, TreeMap und ConcurrentHashMap
  • Arrays aller unterstützten Typen
  • Nullable Versionen aller unterstützten Typen

Benutzerdefinierte Parcelers

Wenn Ihr Typ nicht direkt unterstützt wird, können Sie ein Parceler-Zuordnungsobjekt dafür schreiben.

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

Sie können externe Pakete mit den Annotationen @TypeParceler oder @WriteWith anwenden:

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

Daten aus Grundstück erstellen

Im Java-Code können Sie direkt auf das Feld CREATOR zugreifen.

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

In Kotlin können Sie das Feld CREATOR nicht direkt verwenden. Verwenden Sie stattdessen kotlinx.parcelize.parcelableCreator.

import kotlinx.parcelize.parcelableCreator

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

Eigenschaften bei der Serialisierung überspringen

Wenn Sie ein Attribut überspringen möchten, verwenden Sie die Annotation @IgnoredOnParcel. Sie kann auch für Properties im Body einer Klasse verwendet werden, um Warnungen zu unterdrücken, dass die Property nicht serialisiert wird. Mit @IgnoredOnParcel annotierte Konstruktoreigenschaften müssen einen Standardwert haben.

@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 zum Serialisieren einer Property verwenden

Sie können einen Typ mit @RawValue annotieren, damit Parcelize Parcel.writeValue für dieses Attribut verwendet.

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

Dies kann zur Laufzeit fehlschlagen, wenn der Wert der Property nicht nativ von Android unterstützt wird.

Möglicherweise müssen Sie diese Anmerkung auch verwenden, wenn es keine andere Möglichkeit gibt, die Property zu serialisieren.

Mit versiegelten Klassen und versiegelten Schnittstellen paketieren

Für die Funktion „Parcelize“ darf die zu parzellierende Klasse nicht abstrakt sein. Diese Einschränkung gilt nicht für versiegelte Klassen. Wenn die @Parcelize-Anmerkung für eine versiegelte Klasse verwendet wird, muss sie nicht für die abgeleiteten Klassen wiederholt werden.

@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 für Kotlin Multiplatform einrichten

Vor Kotlin 2.0 konnten Sie Parcelize verwenden, indem Sie Aliasse für Parcelize-Anmerkungen mit expect und actual erstellt haben:

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

In Kotlin 2.0 und höher werden Alias-Anmerkungen, die Plugins auslösen, nicht unterstützt. Um dies zu umgehen, gib stattdessen eine neue Parcelize-Anmerkung als additionalAnnotation-Parameter für das Plug-in an.

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

Da die Parcel-Schnittstelle nur auf Android-Geräten verfügbar ist, generiert Parcelize auf anderen Plattformen keinen Code. Daher können dort vorhandene actual-Implementierungen leer sein. Außerdem können keine Anmerkungen verwendet werden, die im gemeinsamen Code auf die Parcel-Klasse verweisen müssen, z. B. @WriteWith.

Experimentelle Funktionen

Datenklassen-Serializer

Verfügbar seit Kotlin 2.1.0.

Mit der Annotation DataClass können Datenklassen so serialisiert werden, als wären sie selbst mit Parcelize annotiert. Für diese Anmerkung ist die Aktivierung von kotlinx.parcelize.Experimental erforderlich.

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

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

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

Der primäre Konstruktor und alle seine Properties müssen von der Klasse Parcelable aus zugänglich sein. Außerdem müssen alle primären Konstruktoreigenschaften der Datenklasse von Parcelize unterstützt werden. Benutzerdefinierte Parcelers sollten, falls ausgewählt, in der Klasse Parcelable und nicht in der Datenklasse angegeben werden. Wenn die Datenklasse gleichzeitig Serializable implementiert, hat die @DataClass-Anmerkung Vorrang: android.os.Parcel.writeSerializable wird nicht verwendet.

Ein praktischer Anwendungsfall dafür ist die Serialisierung von kotlin.Pair. Ein weiteres nützliches Beispiel ist die Vereinfachung von plattformübergreifendem Code: Üblicher Code könnte die Datenschicht als Datenklassen deklarieren, die der Android-Code dann mit Serialisierungslogik erweitern könnte. Dadurch sind Android-spezifische Anmerkungen und Typaliasse im gemeinsamen Code nicht mehr erforderlich.

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

Nicht val- oder var-Parameter im primären Konstruktor

Verfügbar seit Kotlin 2.1.0.

Wenn Sie diese Funktion aktivieren möchten, fügen Sie den Plugin-Argumenten für „parcelize“ experimentalCodeGeneration=true hinzu.

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

Mit dieser Funktion wird die Beschränkung für primäre Konstruktorargumente aufgehoben, die val oder var sein müssen. Damit wird ein Problem bei der Verwendung von „parcelize“ mit Vererbung gelöst, für das bisher open-Properties erforderlich waren.

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

Solche Parameter dürfen nur in Argumenten für den Konstruktor der Basisklasse verwendet werden. Verweise darauf im Body der Klasse sind nicht zulässig.

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

Feedback

Wenn Probleme mit dem kotlin-parcelize Gradle-Plug-in auftreten, können Sie einen Fehler melden.