Le plug-in kotlin-parcelize
fournit un générateur d'implémentation Parcelable
.
Pour inclure la compatibilité avec Parcelable
, ajoutez le plug-in Gradle au fichier build.gradle
de votre application :
Groovy
plugins { id 'kotlin-parcelize' }
Kotlin
plugins { id("kotlin-parcelize") }
Lorsque vous annotez une classe avec @Parcelize
, une implémentation Parcelable
est automatiquement générée, comme illustré dans l'exemple suivant :
import kotlinx.parcelize.Parcelize
@Parcelize
class User(val firstName: String, val lastName: String, val age: Int): Parcelable
@Parcelize
nécessite que toutes les propriétés sérialisées soient déclarées dans le constructeur principal. Le plug-in émet un avertissement sur chaque propriété avec un champ de support déclaré dans le corps de la classe. De plus, vous ne pouvez pas appliquer @Parcelize
si certains paramètres du constructeur principal ne sont pas des propriétés.
Si votre classe nécessite une logique de sérialisation plus avancée, écrivez-la dans une classe compagnon :
@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
}
}
}
Types acceptés
@Parcelize
accepte un large éventail de types :
- Types primitifs (et leurs versions encapsulées)
- Objets et énumérations
String
,CharSequence
Duration
Exception
Size
,SizeF
,Bundle
,IBinder
,IInterface
,FileDescriptor
SparseArray
,SparseIntArray
,SparseLongArray
,SparseBooleanArray
- Toutes les implémentations
Serializable
(y comprisDate
) etParcelable
- Les collections de tous les types compatibles :
List
(mappé surArrayList
),Set
(mappé surLinkedHashSet
),Map
(mappé surLinkedHashMap
)- Plusieurs implémentations concrètes sont également possibles :
ArrayList
,LinkedList
,SortedSet
,NavigableSet
,HashSet
,LinkedHashSet
,TreeSet
,SortedMap
,NavigableMap
,HashMap
,LinkedHashMap
,TreeMap
,ConcurrentHashMap
- Plusieurs implémentations concrètes sont également possibles :
- Tableaux de tous les types compatibles
- Versions nullable de tous les types compatibles
Parceler
personnalisés
Si votre type n'est pas directement pris en charge, vous pouvez écrire un objet de mappage 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)
}
}
Vous pouvez appliquer des Parceler externes à l'aide d'annotations @TypeParceler
ou @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
Créer des données à partir d'un lot
Dans le code Java, vous pouvez accéder directement au champ CREATOR
.
class UserCreator {
static User fromParcel(Parcel parcel) {
return User.CREATOR.createFromParcel(parcel);
}
}
En Kotlin, vous ne pouvez pas utiliser directement le champ CREATOR
. Utilisez plutôt kotlinx.parcelize.parcelableCreator
.
import kotlinx.parcelize.parcelableCreator
fun userFromParcel(parcel: Parcel): User {
return parcelableCreator<User>().createFromParcel(parcel)
}
Ignorer les propriétés de la sérialisation
Si vous souhaitez que certaines propriétés ne soient pas morcelées, utilisez l'annotation @IgnoredOnParcel
. Il peut également être utilisé sur les propriétés du corps d'une classe pour désactiver les avertissements concernant la non-sérialisation de la propriété.
Les propriétés du constructeur annotées avec @IgnoredOnParcel
doivent avoir une valeur par défaut.
@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
}
Utiliser android.os.Parcel.writeValue pour sérialiser une propriété
Vous pouvez annoter un type avec @RawValue
pour que Parcelize utilise Parcel.writeValue
pour cette propriété.
@Parcelize
class MyClass(val external: @RawValue ExternalClass): Parcelable
Cette opération peut échouer au moment de l'exécution si la valeur de la propriété n'est pas nativement compatible avec Android.
Parcelize peut également vous demander d'utiliser cette annotation lorsqu'il n'existe aucun autre moyen de sérialiser la propriété.
Parceller avec des classes et des interfaces scellées
Parcelize nécessite qu'une classe à parcelliser ne soit pas abstraite. Cette limitation ne s'applique pas aux classes scellées. Lorsque l'annotation @Parcelize
est utilisée sur une classe scellée, elle n'a pas besoin d'être répétée pour les classes dérivées.
@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
Configurer Parcelize pour la multiplateforme Kotlin
Avant la version Kotlin 2.0, vous pouviez utiliser Parcelize en créant un alias pour les annotations Parcelize avec expect
et 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
Dans Kotlin 2.0 et versions ultérieures, la création d'alias pour les annotations qui déclenchent des plug-ins n'est pas prise en charge. Pour contourner ce problème, fournissez plutôt une nouvelle annotation Parcelize
en tant que paramètre additionalAnnotation
au plug-in.
// 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
Étant donné que l'interface Parcel
n'est disponible que sur Android, Parcelize ne génère aucun code sur d'autres plates-formes. Par conséquent, toutes les implémentations actual
peuvent être vides. Il n'est pas non plus possible d'utiliser une annotation qui nécessite de faire référence à la classe Parcel
, par exemple @WriteWith
, dans le code commun.
Fonctionnalités expérimentales
Sérialiseur de classe de données
Cette fonctionnalité est disponible depuis la version 2.1.0 de Kotlin.
L'annotation DataClass
permet de sérialiser les classes de données comme si elles étaient elles-mêmes annotées avec Parcelize
. Cette annotation nécessite l'activation de 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
Le constructeur principal et toutes ses propriétés doivent être accessibles depuis la classe Parcelable
. De plus, toutes les propriétés du constructeur principal de la classe de données doivent être compatibles avec Parcelize
.
Les parcelleurs personnalisés, le cas échéant, doivent être spécifiés sur la classe Parcelable
, et non sur la classe de données.
Si la classe de données implémente Serializable
en même temps, l'annotation @DataClass
est prioritaire : android.os.Parcel.writeSerializable
n'est pas utilisé.
Un cas d'utilisation pratique est la sérialisation de kotlin.Pair
.
Un autre exemple utile est la simplification du code multiplate-forme : le code commun peut déclarer la couche de données en tant que classes de données, que le code Android peut ensuite enrichir avec une logique de sérialisation, ce qui élimine le besoin d'annotations et d'alias de type spécifiques à Android dans le code commun.
// 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
Paramètres non val ou var dans le constructeur principal
Disponible depuis Kotlin 2.1.0.
Pour activer cette fonctionnalité, ajoutez experimentalCodeGeneration=true
aux arguments du plug-in parcelize.
kotlin {
compilerOptions {
// ...
freeCompilerArgs.addAll("-P", "plugin:org.jetbrains.kotlin.parcelize:experimentalCodeGeneration=true")
}
}
Cette fonctionnalité lève la restriction selon laquelle les arguments du constructeur principal doivent être val
ou var
. Cela résout un problème lié à l'utilisation de parcelize avec l'héritage, qui nécessitait auparavant d'utiliser des propriétés 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)
Ces paramètres ne sont autorisés que dans les arguments du constructeur de la classe de base. Il est interdit de les référencer dans le corps de la classe.
@Parcelize
class Derived(s: String): Base(s) { // allowed
@IgnoredOnParcel
val x: String = s // ERROR: not allowed.
init {
println(s) // ERROR: not allowed
}
}
Commentaires
Si vous rencontrez des problèmes avec le plug-in Gradle kotlin-parcelize
, vous veuillez signaler un bug.