I have a set of classes that implement Parcelable
and are annotated by @Parcelize
.
These classes are serialized to byte arrays like this:
return Parcel
.obtain()
.let { parcel ->
writeToParcel(parcel, 0) // "this" is the Parcelable object
parcel
.marshall()
.also { parcel.recycle() }
}
And I want to create a generic deserialization method to create an instance of class from a byte array:
fun <T> create(data: ByteArray): T {
val parcel = parse(data)
TODO("???")
}
private fun parse(data: ByteArray): Parcel {
return Parcel
.obtain()
.apply {
unmarshall(data, 0, data.size)
setDataPosition(0)
}
}
So, how can I read a generic Parcelable
from a Parcel
?
Yes, I know that I can use Parcelable.Creator
, but how should I get it from the code generated by @Parcelize
?
For now, the best generic solution that I've found is achieved using reflection:
internal inline fun <reified T : Parcelable> ByteArray.deserializeParcelable(): T {
val parcel = Parcel
.obtain()
.apply {
unmarshall(this@deserializeParcelable, 0, size)
setDataPosition(0)
}
return parcelableCreator<T>()
.createFromParcel(parcel)
.also {
parcel.recycle()
}
}
internal inline fun <reified T : Parcelable> parcelableCreator(): Parcelable.Creator<T> {
val creator = T::class.java.getField("CREATOR").get(null)
@Suppress("UNCHECKED_CAST")
return creator as Parcelable.Creator<T>
}
We have to use reflection because the CREATOR
generated by @Parcelize
is not accessible in such methods.