O plug-in kotlin-parcelize (link em inglês)
fornece um
gerador de implementação de Parcelable.
Para incluir suporte à interface Parcelable, adicione o plug-in do Gradle ao
arquivo build.gradle do app:
Groovy
plugins { id 'kotlin-parcelize' }
Kotlin
plugins { id("kotlin-parcelize") }
Quando você insere uma anotação com @Parcelize, uma implementação de Parcelable
é gerada automaticamente, como mostrado no exemplo a seguir.
// import kotlinx.parcelize.Parcelize @Parcelize class User(val firstName: String, val lastName: String, val age: Int) : Parcelable
@Parcelize requer que todas as propriedades serializadas sejam declaradas no
construtor principal. O plug-in emite um aviso em cada propriedade
com um campo de backup declarado no corpo da classe. Além disso, não
será possível aplicar @Parcelize se alguns dos parâmetros do construtor principal não
forem propriedades.
Caso sua classe exija uma lógica de serialização mais avançada, escreva-a dentro de uma classe de acompanhamento:
@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 com suporte
@Parcelize aceita uma grande variedade de tipos:
- Tipos primitivos (e suas versões em caixa)
- Objetos e enumerações
String,CharSequenceDurationExceptionSize,SizeF,Bundle,IBinder,IInterface,FileDescriptorSparseArray,SparseIntArray,SparseLongArray,SparseBooleanArray- Todas as implementações de
Serializable(incluindoDate) eParcelable - Coleções de todos os tipos com suporte:
List(mapeado paraArrayList),Set(mapeado paraLinkedHashSet),Map(mapeado paraLinkedHashMap)- Além disso, algumas implementações concretas:
ArrayList,LinkedList,SortedSet,NavigableSet,HashSet,LinkedHashSet,TreeSet,SortedMap,NavigableMap,HashMap,LinkedHashMap,TreeMap,ConcurrentHashMap
- Além disso, algumas implementações concretas:
- Matrizes de todos os tipos com suporte
- Versões anuláveis de todos os tipos com suporte
Parcelers personalizados
Se o tipo não tiver suporte direto, crie um objeto de mapeamento Parceler
para ele.
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) } }
É possível aplicar parcelers externos usando anotações
@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
Criar dados do Parcel
No código Java, é possível acessar o campo CREATOR diretamente.
class UserCreator {
static User fromParcel(Parcel parcel) {
return User.CREATOR.createFromParcel(parcel);
}
}
Em Kotlin, não é possível usar o campo CREATOR diretamente. Em vez disso, use
kotlinx.parcelize.parcelableCreator.
// import kotlinx.parcelize.parcelableCreator fun userFromParcel(parcel: Parcel): User { return parcelableCreator<User>().createFromParcel(parcel) }
Ignorar propriedades da serialização
Se você quiser pular a parcelização de uma propriedade, use a anotação
@IgnoredOnParcel. Também pode ser usado em propriedades dentro do corpo de uma classe para silenciar avisos sobre a propriedade não ser serializada.
As propriedades do construtor anotadas com @IgnoredOnParcel precisam ter um valor padrão.
@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 }
Use android.os.Parcel.writeValue para serializar uma propriedade
Você pode anotar um tipo com @RawValue para que o Parcelize use
Parcel.writeValue nessa propriedade.
@Parcelize class MyClass(val external: @RawValue ExternalClass) : Parcelable
Isso pode falhar no tempo de execução se o valor da propriedade não for compatível nativamente com o Android.
O Parcelize também pode exigir que você use essa anotação quando não houver outra maneira de serializar a propriedade.
Parcelize com classes e interfaces seladas
O Parcelize exige que uma classe a ser parcelada não seja abstrata. Essa limitação não se aplica a classes sealed. Quando a anotação @Parcelize é usada em uma classe selada, ela não precisa ser repetida para as classes 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
Configurar o Parcelize para Kotlin Multiplatform
Antes do Kotlin 2.0, era possível usar o Parcelize criando um alias para as anotações
com 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
No Kotlin 2.0 e versões mais recentes, não há suporte para anotações de alias que acionam plug-ins. Para evitar isso, forneça uma nova anotação Parcelize como o parâmetro additionalAnnotation ao 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
Como a interface Parcel só está disponível no Android, o Parcelize não
gera código em outras plataformas. Portanto, qualquer implementação de actual
pode estar vazia. Também não é possível usar nenhuma anotação que
exija referenciar a classe Parcel, por exemplo
@WriteWith, em código comum.
Recursos experimentais
Serializador de classe de dados
Disponível desde o Kotlin 2.1.0.
A anotação DataClass permite serializar classes de dados como se elas próprias tivessem a anotação Parcelize. Essa anotação requer a ativação da
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
O construtor principal e todas as propriedades dele precisam estar acessíveis na classe
Parcelable. Além disso, todas as propriedades do construtor principal da
classe de dados precisam ser compatíveis com Parcelize.
Os Parcelers personalizados, se escolhidos, precisam ser especificados na classe
Parcelable, não na classe de dados.
Se a classe de dados implementar Serializable ao mesmo tempo, a anotação @DataClass terá prioridade: android.os.Parcel.writeSerializable não será usado.
Um caso de uso prático disso é a serialização de kotlin.Pair.
Outro exemplo útil é a simplificação do código multiplataforma: o código comum pode declarar a camada de dados como classes de dados, que o código do Android pode aumentar com a lógica de serialização, removendo a necessidade de anotações e aliases de tipo específicos do Android no código comum.
// 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 não val ou var no construtor principal
Disponível desde o Kotlin 2.1.0.
Para ativar esse recurso, adicione experimentalCodeGeneration=true aos argumentos do
plug-in parcelize.
kotlin {
compilerOptions {
// ...
freeCompilerArgs.addAll("-P", "plugin:org.jetbrains.kotlin.parcelize:experimentalCodeGeneration=true")
}
}
Esse recurso elimina a restrição de que os argumentos do construtor principal precisam ser
val ou var. Isso resolve um problema de usar parcelize com herança,
que antes exigia o uso de propriedades 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)
Esses parâmetros só podem ser usados em argumentos para o construtor da classe de base. Não é permitido fazer referência a eles no corpo da aula.
@Parcelize
class Derived(s: String): Base(s) { // allowed
@IgnoredOnParcel
val x: String = s // ERROR: not allowed.
init {
println(s) // ERROR: not allowed
}
}
Feedback
Se você encontrar problemas com o plug-in do Gradle kotlin-parcelize,
registre um bug.