Search code examples
androidkotlinannotationsparcelable

Unmarshall Parcelable created by @Parcelize


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?


Solution

  • 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.