Unmarshall Parcelable created by @Parcelize

Viewed 188

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?

1 Answers

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.

Related