3
votes

I get the Error message "Parcelable protocol requires a Parcelable.Creator object called CREATOR on class .....", but I do have a Creator and I don't know what is wrong with it. I copied it from https://developer.android.com/reference/android/os/Parcelable and changed the class name to fit my code. I suspect that the (automatic) conversion from Java to Kotlin wasn't perfect (or to be precise: did it slightly different than it would be needed), but I don't know what the problem is exactly.

There is a Thread with the same Error Message (Parcelable protocol requires a Parcelable.Creator object called CREATOR (I do have CREATOR)) but the problem there was that "fun writeToParcel" did not write in in the same order as it is read in "fun DataHandler". That is not the Problem in my case as it's in the same order.

Another answer there pointed out that it could be a problem that the function needs to be static. However, Kotlin has no "static" function. I read that it's done with "Companion object". I tried that (see below), but it threw another error - and I'm not sure if it would even work.

class DataHandler : Parcelable {

    var player1name = ""
    var player1color = 0

    //main constructor
    fun DataHandler(player1name: String, player1color: Int) {
        this.player1name = player1name
        this.player1color = player1color
    }


    //write object values to parcel for storage
    override fun writeToParcel(dest: Parcel, flags: Int) {
        //write all properties to the parcle
        dest.writeString(player1name)
        dest.writeInt(player1color)
    }

    //constructor used for parcel
    fun DataHandler(parcel: Parcel) {
        //read and set saved values from parcel
        player1name = parcel.readString()
        player1color = parcel.readInt()
    }

    //creator - used when un-parceling our parcle (creating the object)
    val CREATOR: Parcelable.Creator<DataHandler> = object : Parcelable.Creator<DataHandler> {

        override fun createFromParcel(parcel: Parcel): DataHandler {
            return DataHandler(parcel) as DataHandler
        }

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

    //return hashcode of object
    override fun describeContents(): Int {
        return hashCode()
    }
}

This is the sending activity:

        val intentPickPlayer = Intent(this, PlayGame::class.java)

        var dataHandler = DataHandler()
        dataHandler.player1name = "testing"
        intentPickPlayer.putExtra("data", dataHandler)

        startActivity(intentPickPlayer)

This is the receiving activity:

class PlayGame : Activity() {

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.playgame)

    val test = intent.getParcelableExtra<DataHandler>("data")

    Toast.makeText(this, test.player1name, Toast.LENGTH_SHORT).show()

}

Like said above: I tried to make the CREATOR static by putting it into a companion object (apparently that's the way it works in Kotlin, but that produces another error (and I'm not sure if it fixes the first problem)

companion object {
//creator - used when un-parceling our parcle (creating the object)
val CREATOR: Parcelable.Creator<DataHandler> = object : Parcelable.Creator<DataHandler> {

    override fun createFromParcel(parcel: Parcel): DataHandler {
        //HERE COMES AN ERROR: parcel has a red underlining and it says: "too many Arguments for public constructor DataHandler()
        return DataHandler(parcel) as DataHandler
    }

    override fun newArray(size: Int): Array<DataHandler?> {
        return arrayOfNulls<DataHandler>(size)
    }
}
1
Could you try companion object CREATOR : ... ?Lubos Mudrak
Neither: companion object CREATOR : Parcelable.Creator<DataHandler> = object : Parcelable.Creator<DataHandler>{ [fun createFromParcel and fun newArray ] } nor companion object CREATOR :...{ val CREATOR: Parcelable.Creator<DataHandler> = object : Parcelable.Creator<DataHandler> { [fun createFromParcel and fun newArray ]}} worksRaphael Fritz
This: companion object CREATOR : Parcelable.Creator<DataHandler>{ [fun createFromParcel and fun newArray]} looks better, but in the fun createFromParcel is the "parcel" in the return statement underlined in red (Message: "too many Arguments for public constructor DataHandler" - I don't know why because the same code wasn't underlined when it wasn't in a companion object)Raphael Fritz
You have to mark CREATOR with @JvmField.EpicPandaForce

1 Answers

6
votes

In Kotlin constructors are defined by the constructor keyword - https://kotlinlang.org/docs/reference/classes.html

See also https://kotlinlang.org/docs/reference/classes.html#secondary-constructors for info on secondary constructors. Secondary constructors need to delegate to the primary one.

Default constructors are usually defined after the class name and properties defined as part of it:

class DataHandler(var player1name: String, var player1color: Int) : Parcelable {

     //write object values to parcel for storage
    override fun writeToParcel(dest: Parcel, flags: Int) {
        //write all properties to the parcle
        dest.writeString(player1name)
        dest.writeInt(player1color)
    }

    //constructor used for parcel
    constructor(parcel: Parcel) : this(
        //read and set saved values from parcel
        player1name = parcel.readString(),
        player1color = parcel.readInt())

    companion object {
        @JvmField
        //creator - used when un-parceling our parcle (creating the object)
        val CREATOR: Parcelable.Creator = object : Parcelable.Creator {

            override fun createFromParcel(parcel: Parcel): DataHandler {
                return DataHandler(parcel) as DataHandler
            }

            override fun newArray(size: Int): Array {
                return arrayOfNulls(size)
            }
        }
    }

    //return hashcode of object
    override fun describeContents(): Int {
        return hashCode()
    }
}