Search code examples
android-intentandroid-transitionsandroid-bundle

Unmarshalling extras from intent throws an exception but only during onActivityReenter


I just tried to implement onActivityReenter to do a special UI update before the reenter transition runs. I literally took the block for the request code from onActivityResult and split it into 2 parts, one to go into onActivityReenter, the other to stay there:

BEFORE: (onActivityResult)
            if (resultCode == RESULT_OK) {
                // replace the MatchPlaySubmit model
                data?.let {
                    this.dataBinding.submitModel = data.getMatchPlaySubmitModel()
                    val profIndex = data.getMatchPlayFinalProfileIndex()
                    dataBinding.matchPlay.matchPlayPicker.jumpToProfileNumber(profIndex + 1)
                    this.updatePickerView()
                }
            }


override fun onActivityReenter(resultCode: Int, data: Intent?) {
    if (resultCode == RESULT_OK && data != null) {
        // makes sure that the data is from pager, since there no request code
>        if (data.getStringExtra("source") == "mppager") { // causes CRASH
            val profIndex = data.getMatchPlayFinalProfileIndex()
            dataBinding.matchPlay.matchPlayPicker.jumpToProfileNumber(profIndex + 1)
        }
    }
}

AFTER: (onActivityResult)
            if (resultCode == RESULT_OK) {
                // replace the MatchPlaySubmit model
                data?.let {
                    this.dataBinding.submitModel = data.getMatchPlaySubmitModel()
                    this.updatePickerView()
                }
            }

However, the new code onActivityReenter always causes crash. The crash is as if I don't set the classloader properly for the extras. However, I just added to the kotlin extension func to make sure the classloader for the extras is set to mine. However, crash still always happens.

inline internal fun Intent.setMatchPlaySubmitModel(model: MatchPlaySubmitModel?) {
    this.putExtra("@mpsubmit@", model)
    this.setExtrasClassLoader(MatchPlaySubmitModel::class.java.classLoader)
}

android.os.BadParcelableException: ClassNotFoundException when unmarshalling: letstwinkle.com.twinkle.api.MatchPlaySubmitModel at android.os.Parcel.readParcelableCreator(Parcel.java:2535) at android.os.Parcel.readParcelable(Parcel.java:2461) at android.os.Parcel.readValue(Parcel.java:2364) at android.os.Parcel.readArrayMapInternal(Parcel.java:2717) at android.os.BaseBundle.unparcel(BaseBundle.java:269) at android.os.BaseBundle.getString(BaseBundle.java:992) at android.content.Intent.getStringExtra(Intent.java:6211) at letstwinkle.com.twinkle.MatchPlayActivity.onActivityReenter(MatchPlayActivity.kt:1123)

Tested API 24


Solution

  • It appears that the framework transition implementation has a goof-up. There is no "good" solution at this time. The possible workarounds are just any way to avoid putting your app-defined class into the result intent. The way I chose was for that class (fortunately there was just one), remove the Parcelable implementation and provide a constructor from an Intent and a writeToIntent method:

    constructor(intent: Intent) {
        playID = intent.getStringExtra("@mpsubmit@playid")
        chosenProfileID = intent.getStringExtra("@mpsubmit@prof1")
        extraChosenProfileID = intent.getStringExtra("@mpsubmit@prof2")
        passed = intent.getBooleanExtra("@mpsubmit@pass", false)
        shout = intent.getStringExtra("@mpsubmit@shout1")
        extraShout = intent.getStringExtra("@mpsubmit@shout2")
    }
    fun writeToIntent(intent: Intent) {
        intent.putExtra("@mpsubmit@playid", playID)
        intent.putExtra("@mpsubmit@prof1", chosenProfileID)
        intent.putExtra("@mpsubmit@prof2", extraChosenProfileID)
        intent.putExtra("@mpsubmit@pass", passed)
        intent.putExtra("@mpsubmit@shout1", shout)
        intent.putExtra("@mpsubmit@shout2", extraShout)
    }