Il plug-in kotlin-parcelize fornisce un generatore di implementazione Parcelable.
Per includere il supporto di Parcelable, aggiungi il plug-in Gradle al file Parcelable dell'app:build.gradle
Groovy
plugins { id 'kotlin-parcelize' }
Kotlin
plugins { id("kotlin-parcelize") }
Quando annoti una classe con @Parcelize, viene generata automaticamente un'implementazione di Parcelable, come mostrato nell'esempio seguente:
import kotlinx.parcelize.Parcelize
@Parcelize
class User(val firstName: String, val lastName: String, val age: Int): Parcelable
@Parcelize richiede che tutte le proprietà serializzate siano dichiarate nel
costruttore principale. Il plug-in emette un avviso per ogni proprietà con un campo di supporto dichiarato nel corpo della classe. Inoltre, non puoi
applicare @Parcelize se alcuni dei parametri del costruttore principale non sono
proprietà.
Se la classe richiede una logica di serializzazione più avanzata, scrivila in una classe complementare:
@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
}
}
}
Tipi supportati
@Parcelize supporta una vasta gamma di tipi:
- Tipi primitivi (e le relative versioni con casella)
- Oggetti ed enum
String,CharSequenceDurationExceptionSize,SizeF,Bundle,IBinder,IInterfaceeFileDescriptorSparseArray,SparseIntArray,SparseLongArray,SparseBooleanArray- Tutte le implementazioni di
Serializable(inclusoDate) eParcelable - Raccolte di tutti i tipi supportati:
List(mappata aArrayList),Set(mappata aLinkedHashSet),Map(mappata aLinkedHashMap)- Inoltre, una serie di implementazioni concrete:
ArrayList,LinkedList,SortedSet,NavigableSet,HashSet,LinkedHashSet,TreeSet,SortedMap,NavigableMap,HashMap,LinkedHashMap,TreeMap,ConcurrentHashMap
- Inoltre, una serie di implementazioni concrete:
- Array di tutti i tipi supportati
- Versioni con valori null di tutti i tipi supportati
Parceler personalizzati
Se il tipo non è supportato direttamente, puoi scrivere un Parceler
oggetto di mappatura per questo.
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)
}
}
Puoi applicare parcellatori esterni utilizzando le annotazioni @TypeParceler o @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
Creare dati da Parcel
Nel codice Java, puoi accedere direttamente al campo CREATOR.
class UserCreator {
static User fromParcel(Parcel parcel) {
return User.CREATOR.createFromParcel(parcel);
}
}
In Kotlin, non puoi utilizzare direttamente il campo CREATOR. Usa invece
kotlinx.parcelize.parcelableCreator.
import kotlinx.parcelize.parcelableCreator
fun userFromParcel(parcel: Parcel): User {
return parcelableCreator<User>().createFromParcel(parcel)
}
Saltare le proprietà dalla serializzazione
Se vuoi saltare il parcellamento di alcune proprietà, utilizza l'annotazione @IgnoredOnParcel. Può essere utilizzato anche per le proprietà all'interno del corpo di una classe per disattivare gli avvisi relativi alla mancata serializzazione della proprietà.
Le proprietà del costruttore annotate con @IgnoredOnParcel devono avere un valore predefinito.
@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
}
Utilizzare android.os.Parcel.writeValue per la serializzazione di una proprietà
Puoi annotare un tipo con @RawValue per fare in modo che l'opzione Parcelize utilizzi
Parcel.writeValue per quella proprietà.
@Parcelize
class MyClass(val external: @RawValue ExternalClass): Parcelable
L'operazione potrebbe non riuscire in fase di runtime se il valore della proprietà non è supportato in modo nativo da Android.
La suddivisione in lotti potrebbe anche richiedere l'utilizzo di questa annotazione quando non esiste un altro modo per serializzare la proprietà.
Pacchettizza i dati con classi e interfacce sigillate
La funzionalità Parcelize richiede che una classe da suddividere non sia astratta. Questa limitazione non vale per le classi sigillate. Quando l'annotazione @Parcelize viene utilizzata in una
classe protetta, non deve essere ripetuta per le classi risultanti.
@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
Configurare Parcelize per Kotlin multipiattaforma
Prima di Kotlin 2.0, potevi usare la funzionalità Parcelize mediante l'aliasing delle annotazioni Parcelize
con expect e 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
In Kotlin 2.0 e versioni successive, le annotazioni di alias che attivano i plug-in non sono supportate. Per aggirare questo problema, fornisci al plug-in una nuova annotazione Parcelize come
parametro 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
Poiché l'interfaccia Parcel è disponibile solo su Android, Parcelize non genererà codice su altre piattaforme, pertanto eventuali implementazioni di Parcel possono essere vuote.actual Inoltre, non è possibile utilizzare nel codice comune annotazioni che richiedono il riferimento alla classe Parcel, ad esempio @WriteWith.
Funzionalità sperimentali
Serializzatore della classe di dati
Disponibile da Kotlin 2.1.0.
L'annotazione DataClass consente di serializzare le classi di dati come se fossero annotate con Parcelize. Questa annotazione richiede l'attivazione di 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
Il costruttore principale e tutte le sue proprietà devono essere accessibili dalla classe
Parcelable. Inoltre, tutte le proprietà del costruttore principale della classe di dati devono essere supportate da Parcelize.
I collettivi personalizzati, se scelti, devono essere specificati nella classe Parcelable, non nella classe dei dati.
Se la classe di dati implementa Serializable contemporaneamente, l'annotazione @DataClass
ha la priorità:
android.os.Parcel.writeSerializable
non verrà utilizzata.
Un caso d'uso pratico è la serializzazione di kotlin.Pair.
Un altro esempio utile è la semplificazione del codice multipiattaforma: il codice comune potrebbe dichiarare il livello dati come classi di dati, che il codice Android potrebbe poi integrare con la logica di serializzazione, eliminando la necessità di annotazioni e alias di tipo specifici per Android nel codice comune.
// 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
Parametri non val o var nel costruttore principale
Disponibile da Kotlin 2.1.0.
Per attivare questa funzionalità, aggiungi experimentalCodeGeneration=true agli argomenti del plug-in parcelize.
kotlin {
compilerOptions {
// ...
freeCompilerArgs.addAll("-P", "plugin:org.jetbrains.kotlin.parcelize:experimentalCodeGeneration=true")
}
}
Questa funzionalità rimuove la limitazione che prevede che gli argomenti del costruttore principale debbano essere
val o var. In questo modo viene risolto uno dei problemi dell'utilizzo di parcelize con l'eredità,
che in precedenza richiedeva l'utilizzo delle proprietà 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)
Questi parametri possono essere utilizzati solo negli argomenti per il costruttore della classe base. Non è consentito fare riferimento a questi contenuti nel corpo del corso.
@Parcelize
class Derived(s: String): Base(s) { // allowed
@IgnoredOnParcel
val x: String = s // ERROR: not allowed.
init {
println(s) // ERROR: not allowed
}
}
Feedback
In caso di problemi con il kotlin-parcelizeplugin Gradle, puoi
segnalare un bug.