Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Parcel.readTypedList() along with @Parcelize from kotlin-android-extensions?

I'm running into a problem with implementing Parcelable-based state persistence in a View. Namely, as I need to implement the methods of BaseSavedState like this:

class SavedState : BaseSavedState {
    lateinit var myList: ArrayList<MyParcelable>

    constructor(superState: Parcelable) : super(superState) {}

    private constructor(parcel: Parcel) : super(parcel) {
        val listSize = parcel.readInt()
        val list = ArrayList<MyParcelable>(listSize)
        this.myList = parcel.readTypedList(list, /* needs MyParcelable.CREATOR */ )
    }

    override fun writeToParcel(out: Parcel, flags: Int) {
        super.writeToParcel(out, flags)
        out.writeInt(myList.size())
        out.writeTypedList(myList)
    }

    companion object {
        @JvmField
        val CREATOR: Parcelable.Creator<SavedState> = object : Parcelable.Creator<SavedState> {
            override fun createFromParcel(`in`: Parcel): SavedState {
                return SavedState(`in`)
            }

            override fun newArray(size: Int): Array<SavedState?> {
                return arrayOfNulls(size)
            }
        }
    }
}

However, as you can see, I can't seem to use Parcel.readTypedList() because it needs both the typed list, and the parcelable's CREATOR field!

Classes marked like:

@Parcelize
data class MyParcelable(val someData: String): Parcelable {
}

Do not have CREATORs, in fact, if you try to implement, it says:

'CREATOR' definition is not allowed. Use 'Parceler' companion object instead.

I do not want a Parceler companion object, I need a CREATOR!

I also kinda don't want to write the Parcelable implementation by hand, so I might just make it Serializable and be like "whatever".


Does anyone know by chance how you can use Parcel.readTypedList() with @Parcelize-annotated Kotlin data class?

like image 645
EpicPandaForce Avatar asked Aug 11 '18 11:08

EpicPandaForce


3 Answers

https://github.com/JetBrains/kotlin/commit/fc013c6b9f5f3083d06385803ff687dc9a6ab4f0#

From Kotlin 1.6.21, we can use parcelableCreator() from

import kotlinx.parcelize.parcelableCreator

val dataClass = TestDataClass("someValue")
val creator = parcelableCreator<TestDataClass>()
val parcel: Parcel? = null
parcel?.createTypedArrayList(creator) // Will compile
like image 157
Bagadeshkumar R Avatar answered Oct 06 '22 00:10

Bagadeshkumar R


It's a bug on the Parcelize plugin like you mentioned https://youtrack.jetbrains.com/issue/KT-19853 and it can be solved by either reflection or actually, you can have a proxy class to java as you can access that CREATOR field in java without any problems.

I made a util java class for all creators and then made a kotlin extension referring to that creator

Java

@NonNull
public static Parcelable.Creator<Blah> getBlahCreator() {
    return Blah.CREATOR;
}

Kotlin

val Blah.Companion.CREATOR: Parcelable.Creator<Blah>
 get() = UtilClass.getBlahCreator()

And now I can access Blah.CREATOR anywhere! And those two classes that have all the creators and extensions could be just deleted later after the bug got fixed in the foreseeable future and it'll just work as expected without any edits in the other classes that use the CREATOR

This feels much better to me than the reflection solution! but either way, they are both disgusting and I hope this bug could be fixed soon enough!

like image 20
Ahmed Hegazy Avatar answered Oct 05 '22 23:10

Ahmed Hegazy


Upvoted for the question as I encounter the same problem in one of class, but its not visible as I am a new user. I used the below alternative,(readArrayList)though it has are minor difference

readTypedList
arrayListOf<YourParcelableClass>().apply { parcel.readArrayList(YourParcelableClass::class.java.classLoader) }

Thanks for accepted solution but not sure why, even in java class file I am not able to read the CREATOR field.

like image 34
PrashanthR Avatar answered Oct 05 '22 23:10

PrashanthR