Wtyczka kotlin-parcelize
udostępnia generator implementacji Parcelable
.
Aby uwzględnić obsługę Parcelable
, dodaj wtyczkę Gradle do pliku build.gradle
aplikacji:
Odlotowe
plugins { id 'kotlin-parcelize' }
Kotlin
plugins { id("kotlin-parcelize") }
Gdy dodasz adnotację do klasy za pomocą @Parcelize
, implementacja Parcelable
zostanie wygenerowana automatycznie, jak 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 głównym konstruktorze. Wtyczka wyświetla ostrzeżenie w przypadku każdej właściwości z polem pomocniczym zadeklarowanym w ciele klasy. Nie możesz też zastosować @Parcelize
, jeśli niektóre parametry konstruktora głównego nie są właściwościami.
Jeśli klasa wymaga bardziej zaawansowanej logiki serializacji, wpisz 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 wiele typów:
- typy proste (i ich wersje w pudełku);
- Obiekty i typy enumeracji
String
,CharSequence
Duration
Exception
Size
,SizeF
,Bundle
,IBinder
,IInterface
,FileDescriptor
SparseArray
,SparseIntArray
,SparseLongArray
,SparseBooleanArray
- Wszystkie implementacje
Serializable
(w tymDate
) iParcelable
- Kolekcje wszystkich obsługiwanych typów:
List
(zmapowane naArrayList
),Set
(zmapowane naLinkedHashSet
),Map
(zmapowane naLinkedHashMap
)- Dotyczy to też kilku konkretnych implementacji:
ArrayList
,LinkedList
,SortedSet
,NavigableSet
,HashSet
,LinkedHashSet
,TreeSet
,SortedMap
,NavigableMap
,HashMap
,LinkedHashMap
,TreeMap
,ConcurrentHashMap
- Dotyczy to też kilku konkretnych implementacji:
- tablice wszystkich obsługiwanych typów;
- wersje z możliwością przypisania wartości wszystkich obsługiwanych typów;
Niestandardowe Parceler
Jeśli Twój typ nie jest obsługiwany bezpośrednio, możesz napisać dla niego obiekt mapowania 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)
}
}
Możesz stosować zewnętrzne funkcje parcelujące 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 bezpośrednio uzyskać dostęp do pola CREATOR
.
class UserCreator {
static User fromParcel(Parcel parcel) {
return User.CREATOR.createFromParcel(parcel);
}
}
W języku Kotlin nie można bezpośrednio używać pola CREATOR
. Zamiast tego używaj elementu 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ąć pewną właściwość podczas parcelacji, użyj adnotacji @IgnoredOnParcel
. Można go też używać w przypadku właściwości w ciele klasy, aby wyciszyć ostrzeżenia o tym, że dana 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ć adnotację do typu za pomocą @RawValue
, aby usługa Parcelize używała w przypadku tej usługi wartości Parcel.writeValue
.
@Parcelize
class MyClass(val external: @RawValue ExternalClass): Parcelable
Może się to nie udać w czasie działania, jeśli wartość właściwości nie jest obsługiwana natywnie przez Androida.
Parcelize może też wymagać użycia tej adnotacji, jeśli nie ma innego sposobu na serializację właściwości.
dzielenie pakietów za pomocą zamkniętych klas i zamkniętych interfejsów;
Funkcja parcelize wymaga, aby klasa, którą ma być zapakowana, nie była abstrakcyjna. To ograniczenie nie dotyczy klas zamkniętych. Gdy adnotacja @Parcelize
jest używana w przypadku zamkniętej klasy, 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 w przypadku Kotlin Multiplatform
Przed Kotlinem 2.0 można było używać Parcelize, zastępując adnotacje Parcelize za pomocą expect
i 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
W wersji 2.0 i nowszych języka Kotlin nie obsługuje adnotacji aliasów, które wywołują wtyczki. Aby to obejść, zamiast parametru additionalAnnotation
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 wygeneruje kodu na innych platformach, a implementacje actual
mogą być puste. Nie można też używać żadnych adnotacji, które wymagają odwołania do klasy Parcel
, np. @WriteWith
, w ramach wspólnego kodu.
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 opatrzone adnotacjami Parcelize
. Ta adnotacja wymaga włączenia 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
Główny konstruktor i wszystkie jego właściwości muszą być dostępne z klasy Parcelable
. Dodatkowo wszystkie właściwości konstruktora głównego klasy danych muszą być obsługiwane przez Parcelize
.
Niestandardowe usługi dostawy, jeśli zostały wybrane, powinny być określone w klasie Parcelable
, nie w klasie danych.
Jeśli klasa danych implementuje tag Serializable
w tym samym czasie, tag @DataClass
ma pierwszeństwo: tag android.os.Parcel.writeSerializable
nie będzie używany.
Przykładem praktycznego zastosowania jest serializacja kotlin.Pair
.
Innym przydatnym przykładem jest uproszczenie kodu wieloplatformowego: wspólny kod może deklarować warstwę danych jako klasy danych, które kod Androida może następnie rozszerzyć o logikę serializacji, co eliminuje potrzebę stosowania adnotacji i aliasów typów specyficznych dla Androida w wspólnym kodzie.
// 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 głównym konstruktorze
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, które wymaga, aby argumenty konstruktora głównego były typu val
lub var
. Rozwiązaliśmy w ten sposób jeden z problemów związanych z używaniem funkcji parcelize z dziedziczeniem, która wcześniej wymagała używania 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 można używać tylko w argumentach konstruktora klasy bazowej. Odwoływanie się do nich w treści zajęć jest niedozwolone.
@Parcelize
class Derived(s: String): Base(s) { // allowed
@IgnoredOnParcel
val x: String = s // ERROR: not allowed.
init {
println(s) // ERROR: not allowed
}
}
Opinia
Jeśli napotkasz problemy z wtyczką kotlin-parcelize
Gradle, możesz zgłosić błąd.