Saturday, 20 April 2019

How to find source of crashes of type java.lang.RuntimeException: Parcel android.os.Parcel@####: Unmarshalling unknown type code XXXX at offset YYY

Our crash reporting system is logging crashes of this type:

Caused by java.lang.RuntimeException: Parcel android.os.Parcel@8bf0d1f: Unmarshalling unknown type code 6881391 at offset 356
   at android.os.Parcel.readValue(Parcel.java:2779)
   at android.os.Parcel.readSparseArrayInternal(Parcel.java:3148)
   at android.os.Parcel.readSparseArray(Parcel.java:2362)
   at android.os.Parcel.readValue(Parcel.java:2757)
   at android.os.Parcel.readArrayMapInternal(Parcel.java:3067)
   at android.os.BaseBundle.unparcel(BaseBundle.java:257)
   at android.os.Bundle.getSparseParcelableArray(Bundle.java:958)
   at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1329)
   at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1759)
   at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1827)
   at android.support.v4.app.FragmentManagerImpl.dispatchStateChange(FragmentManager.java:3244)
   at android.support.v4.app.FragmentManagerImpl.dispatchCreate(FragmentManager.java:3194)
   at android.support.v4.app.Fragment.restoreChildFragmentState(Fragment.java:1444)
   at android.support.v4.app.Fragment.onCreate(Fragment.java:1415)
   at com.payments.base.BaseFragment.onCreate(BaseFragment.java:68)
   at com.payments.app.fragments.TopLevelFragment.onCreate(TopLevelFragment.java:422)
   at android.support.v4.app.Fragment.performCreate(Fragment.java:2331)
   at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1386)
   at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1759)
   at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1827)
   at android.support.v4.app.FragmentManagerImpl.dispatchStateChange(FragmentManager.java:3244)
   at android.support.v4.app.FragmentManagerImpl.dispatchCreate(FragmentManager.java:3194)
   at android.support.v4.app.Fragment.restoreChildFragmentState(Fragment.java:1444)
   at android.support.v4.app.Fragment.onCreate(Fragment.java:1415)

What I know: This occurs when app is restored from background, and onCreate is called. Our app is a Single-Activity app, with all UI managed by fragments. This crash is infrequent, and very hard to reproduced in our dev environment.

Also - I don't believe the cause is some parcelable object we created, rather Android component reloading, but not sure.

What I'd like to know: how to analyze these stacktraces to pinpoint the cause? how to make use of the little data given?

Note that the stacktrace hardly points to any particular line in our app, and where it does - only to the onCreate methods of our activity and fragments base classes



from How to find source of crashes of type java.lang.RuntimeException: Parcel android.os.Parcel@####: Unmarshalling unknown type code XXXX at offset YYY

1 comment:

  1. Unknown: First off, use the developer settings to make the activity go away right as it is backgrounded. Hopefully, that will make the crash more reproducible.

    Then, you are correct that the error here IS in a standard component. But, what you do in onSaveInstanceState, or rather when writing to the parcel for a parcelable object, CAN cause this exception. If you are writing something, but not reading it back, then the next object to read from the parcel (in this case, a system object), will have this error.

    I just had to track down this exact error, and in my case a 3rd party Date Picker library was not doing this correctly. Once the crash was reproducible for me, I commented out various components until the crash stopped. Then I put breakpoints in all of the 3rd party library's onSaveInstanceState and onRestoreInstanceState methods, figuring out which ones never had their restore called. Then I knew the problem with in objects that were among those whose restore WAS called.

    Then I looked at the code for mismatches between writes and reads to the parcel.

    They had this for the write:
    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
    super.writeToParcel(dest, flags);

    if (sCurrentPicker != null) {
    dest.writeString(sCurrentPicker.name());
    dest.writeString(sHiddenPicker.name());
    dest.writeString(sCurrentRecurrenceOption.name());
    dest.writeString(sRecurrenceRule);
    }
    }

    But this for the read:

    private SavedState(Parcel in) {
    super(in);

    String currentPicker = in.readString();
    if (currentPicker != null) {
    sCurrentPicker = SublimeOptions.Picker.valueOf(currentPicker);
    sHiddenPicker = SublimeOptions.Picker.valueOf(in.readString());
    sCurrentRecurrenceOption = SublimeRecurrencePicker.RecurrenceOption.valueOf(in.readString());
    sRecurrenceRule = in.readString();
    }
    else {
    sCurrentPicker = SublimeOptions.Picker.DATE_PICKER;
    sHiddenPicker = SublimeOptions.Picker.DATE_PICKER;
    sCurrentRecurrenceOption = SublimeRecurrencePicker.RecurrenceOption.DAILY;
    sRecurrenceRule = null;
    }
    }

    Notice how they only some times do a writeString for sCurrentPicker.name(), but ALWAYS do a readString? I ended up adding an else clause that did a writeString(null) in the case that sCurrentPicker was null.

    ReplyDelete