Wtyczka kotlin-parcelize
udostępnia generator implementacji Parcelable
.
Aby uwzględnić obsługę języka Parcelable
, dodaj wtyczkę Gradle do pliku build.gradle
aplikacji:
Groovy
plugins { id 'kotlin-parcelize' }
Kotlin
plugins { id("kotlin-parcelize") }
Gdy dodasz adnotację do klasy za pomocą @Parcelize
, zostanie automatycznie wygenerowana implementacja Parcelable
, jak w tym przykładzie:
import kotlinx.parcelize.Parcelize
@Parcelize
class User(val firstName: String, val lastName: String, val age: Int): Parcelable
Funkcja @Parcelize
wymaga, by wszystkie zserializowane 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, zaimplementuj 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 podstawowe (i ich wersje pudełkowe)
- Obiekty i typy enumeracji
String
,CharSequence
Duration
Exception
Size
,SizeF
,Bundle
,IBinder
,IInterface
,FileDescriptor
SparseArray
,SparseIntArray
,SparseLongArray
,SparseBooleanArray
- Wszystkie implementacje
Serializable
(w tymDate
) iParcelable
- Zbiory wszystkich obsługiwanych typów:
List
(mapowane naArrayList
),Set
(mapowane naLinkedHashSet
),Map
(mapowane naLinkedHashMap
).- Kilka konkretnych implementacji:
ArrayList
,LinkedList
,SortedSet
,NavigableSet
,HashSet
,LinkedHashSet
,TreeSet
,SortedMap
,NavigableMap
,HashMap
,LinkedHashMap
,TreeMap
,ConcurrentHashMap
- Kilka 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ąć wyodrębnianie jakiejś właściwości, 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 opatrzone adnotacjami @IgnoredOnParcel
muszą mieć domyślną wartość.
@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
W czasie działania może to się nie udać, jeśli wartość właściwości nie jest natywnie obsługiwana przez Androida.
Parcelize może też wymagać użycia tej adnotacji, jeśli nie ma innego sposobu na serializację właściwości.
Zadbaj o komponent z zastosowanymi klasami i zamkniętymi interfejsami
Funkcja parcelize wymaga, aby klasa, którą ma być zapakowana, nie była abstrakcyjna. To ograniczenie nie dotyczy zajęć 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ć adnotacji, które wymagają odwołania do klasy Parcel
, na przykład @WriteWith
, w kodzie ogólnym.
Funkcje eksperymentalne
Serializator klasy danych
Dostępne od wersji Kotlin 2.1.0.
Adnotacja DataClass
umożliwia serializowanie klas danych w taki sposób, jakby same były oznaczane 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: powszechny kod może zadeklarować warstwę danych jako klasy danych. W takim przypadku kod na Androida można uzupełnić logiką serializacji, eliminując potrzebę stosowania adnotacji związanych z Androidem i aliasów typów we 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 konstrukcie głównym
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ązuje to jeden problem 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)
Takich parametrów można używać tylko w argumentach w konstruktorze klasy podstawowej. 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ą Gradle kotlin-parcelize
, możesz zgłosić błąd.