El complemento kotlin-parcelize proporciona un generador de implementaciones Parcelable.
Para incluir compatibilidad con Parcelable, agrega el complemento de Gradle al archivo build.gradle de tu app:
Groovy
plugins { id 'kotlin-parcelize' }
Kotlin
plugins { id("kotlin-parcelize") }
Cuando anotas una clase con @Parcelize, se genera automáticamente una implementación Parcelable, como se muestra en el siguiente ejemplo:
// import kotlinx.parcelize.Parcelize @Parcelize class User(val firstName: String, val lastName: String, val age: Int) : Parcelable
@Parcelize requiere que todas las propiedades serializadas se declaren en el constructor principal. El complemento emite una advertencia en cada propiedad con un campo de respaldo declarado en el cuerpo de la clase. Además, no puedes aplicar @Parcelize si algunos de los parámetros del constructor principal no son propiedades.
Si tu clase requiere una lógica de serialización más avanzada, escríbela dentro de una clase complementaria:
@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 } } }
Tipos admitidos
@Parcelize admite una amplia variedad de tipos:
- Tipos primitivos (y sus versiones encuadrados)
- Objetos y enumeraciones
String,CharSequenceDurationExceptionSize,SizeF,Bundle,IBinder,IInterface,FileDescriptorSparseArray,SparseIntArray,SparseLongArray,SparseBooleanArray- Todas las implementaciones de
Serializable(incluidasDate) yParcelable - Colecciones de todos los tipos compatibles:
List(mapeado aArrayList),Set(mapeado aLinkedHashSet),Map(mapeado aLinkedHashMap)- También hay una cantidad de implementaciones concretas:
ArrayList,LinkedList,SortedSet,NavigableSet,HashSet,LinkedHashSet,TreeSetySortedMap,NavigableMap,HashMap,LinkedHashMap,TreeMap,ConcurrentHashMap
- También hay una cantidad de implementaciones concretas:
- Arreglos de todos los tipos compatibles
- Versiones anulables de todos los tipos compatibles
Parceler personalizados
Si tu tipo no se admite directamente, puedes escribir un objeto de mapeo 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) } }
Puedes aplicar paquetes externos mediante las anotaciones @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
Crea datos a partir de Parcel
En el código Java, puedes acceder al campo CREATOR directamente.
class UserCreator {
static User fromParcel(Parcel parcel) {
return User.CREATOR.createFromParcel(parcel);
}
}
En Kotlin, no puedes usar el campo CREATOR directamente. En su lugar, usa kotlinx.parcelize.parcelableCreator.
// import kotlinx.parcelize.parcelableCreator fun userFromParcel(parcel: Parcel): User { return parcelableCreator<User>().createFromParcel(parcel) }
Cómo omitir propiedades de la serialización
Si quieres omitir la serialización de alguna propiedad, usa la anotación @IgnoredOnParcel. También se puede usar en propiedades dentro del cuerpo de una clase para silenciar las advertencias sobre la propiedad que no se serializa.
Las propiedades del constructor anotadas con @IgnoredOnParcel deben tener un valor predeterminado.
@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 }
Usa android.os.Parcel.writeValue para serializar una propiedad
Puedes anotar un tipo con @RawValue para que Parcelize use Parcel.writeValue para esa propiedad.
@Parcelize class MyClass(val external: @RawValue ExternalClass) : Parcelable
Esto podría fallar en el tiempo de ejecución si el valor de la propiedad no es compatible de forma nativa con Android.
Es posible que Parcelize también te exija usar esta anotación cuando no haya otra forma de serializar la propiedad.
Cómo dividir en paquetes con clases y interfaces selladas
Parcelize requiere que la clase que se va a parcelar no sea abstracta. Esta limitación no se aplica a las clases selladas. Cuando se usa la anotación @Parcelize en una clase sellada, no es necesario repetirla para las clases derivadas.
@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
Cómo configurar Parcelize para Kotlin Multiplatform
Antes de Kotlin 2.0, podías usar Parcelize creando alias de las anotaciones de Parcelize con expect y 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
En Kotlin 2.0 y versiones posteriores, no se admiten las anotaciones de alias que activan complementos. Para evitar esto, proporciona una nueva anotación Parcelize como el parámetro additionalAnnotation del complemento.
// 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
Dado que la interfaz Parcel solo está disponible en Android, Parcelize no generará ningún código en otras plataformas, por lo que cualquier implementación de actual allí puede estar vacía. Tampoco es posible usar ninguna anotación que requiera hacer referencia a la clase Parcel, por ejemplo, @WriteWith, en el código común.
Funciones experimentales
Serializador de clase de datos
Disponible desde Kotlin 2.1.0.
La anotación DataClass permite serializar clases de datos como si estuvieran anotadas con Parcelize. Esta anotación requiere que se habilite 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
Se debe poder acceder al constructor principal y a todas sus propiedades desde la clase Parcelable. Además, Parcelize debe admitir todas las propiedades del constructor principal de la clase de datos.
Si se eligen Parceladores personalizados, se deben especificar en la clase Parcelable, no en la clase de datos.
Si la clase de datos implementa Serializable al mismo tiempo, la anotación @DataClass tiene prioridad: no se usará android.os.Parcel.writeSerializable.
Un caso de uso práctico para esto es la serialización de kotlin.Pair.
Otro ejemplo útil es la simplificación del código multiplataforma: El código común podría declarar la capa de datos como clases de datos, que el código de Android podría aumentar con lógica de serialización, lo que eliminaría la necesidad de anotaciones específicas de Android y alias de tipo en el código común.
// 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
Parámetros que no son val o var en el constructor principal
Disponible desde Kotlin 2.1.0.
Para habilitar esta función, agrega experimentalCodeGeneration=true a los argumentos del complemento parcelize.
kotlin {
compilerOptions {
// ...
freeCompilerArgs.addAll("-P", "plugin:org.jetbrains.kotlin.parcelize:experimentalCodeGeneration=true")
}
}
Esta función levanta la restricción de que los argumentos del constructor principal deban ser val o var. Esto resuelve un punto problemático del uso de parcelize con herencia, que antes requería el uso de propiedades 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)
Solo se permite usar estos parámetros en argumentos para el constructor de la clase base. No se permite hacer referencia a ellos en el cuerpo de la clase.
@Parcelize
class Derived(s: String): Base(s) { // allowed
@IgnoredOnParcel
val x: String = s // ERROR: not allowed.
init {
println(s) // ERROR: not allowed
}
}
Comentarios
Si tienes algún problema con el complemento kotlin-parcelize de Gradle, informa un error.