Parcel Unmarshalling unknown type code

Manoj Gopal picture Manoj Gopal · Oct 30, 2013 · Viewed 20.7k times · Source

We are getting this error in the Crash reports logged by play store. Cant replicate this in all our testing. Does anyone else have the same problem or solution ? Thing is, we dont even know what to do to replicate this bug.

All Parcelable objects have CREATOR, writeToParcel() and contructor defined. All Lists and complex types are initialised and null checked.

java.lang.RuntimeException: Unable to start activity ComponentInfo{au.com.company/au.com.company.DetailsActivity}: java.lang.RuntimeException: Parcel android.os.Parcel@42d6e270: Unmarshalling unknown type code 6881381 at offset 11268
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2247)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2297)
at android.app.ActivityThread.access$700(ActivityThread.java:152)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1282)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5328)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1102)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:869)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.RuntimeException: Parcel android.os.Parcel@42d6e270: Unmarshalling unknown type code 6881381 at offset 11268
at android.os.Parcel.readValue(Parcel.java:2032)
at android.os.Parcel.readMapInternal(Parcel.java:2225)
at android.os.Bundle.unparcel(Bundle.java:223)
at android.os.Bundle.getSparseParcelableArray(Bundle.java:1240)
at android.support.v4.app.FragmentManagerImpl.moveToState(SourceFile:861)
at android.support.v4.app.FragmentManagerImpl.moveToState(SourceFile:1104)
at android.support.v4.app.FragmentManagerImpl.moveToState(SourceFile:1086)
at android.support.v4.app.FragmentManagerImpl.dispatchCreate(SourceFile:1872)
at android.support.v4.app.FragmentActivity.onCreate(SourceFile:215)
at android.support.v7.app.ActionBarActivity.onCreate(SourceFile:97)
at au.com.company.DetailsActivity.onCreate(SourceFile:40)
at android.app.Activity.performCreate(Activity.java:5250)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1097)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2211)
... 11 more

Answer

Ricardo picture Ricardo · Nov 14, 2013

I was having the issue with a custom view having its own implementation of View.BaseSavedState. It turned out that the writeToParcel() method there had method calls in the wrong order.

It was annoying to find the problem and I needed to step debug the Android SDK all the way to the Bundle class, where unparcel() is called:

synchronized void unparcel() {
    if (mParcelledData == null) {
        return;
    }

    int N = mParcelledData.readInt();
    if (N < 0) {
        return;
    }
    if (mMap == null) {
        mMap = new HashMap<String, Object>(N);
    }
    mParcelledData.readMapInternal(mMap, N, mClassLoader);
    mParcelledData.recycle();
    mParcelledData = null;
}

Line 223 of your log is exactly the readMapInternal() call. There the Parcel object will iterate through its items, reading the values one by one. The problem usually occurs if something was written in the wrong order. The method Parcel.readValue(ClassLoader loader) will read them in the order they were written but if you have custom views or objects, if they were written in an order different than the one you are now reading them, something wrong will happen in the NEXT item to read.

So I found the problem by identifying the last item that was read successfully. You can usually identify that (if it's a custom view or object) by checking its creator class name. Check the Parcel class' method readParcelableCreator():

public final <T extends Parcelable> Parcelable.Creator<T> readParcelableCreator(
        ClassLoader loader) {
    String name = readString();
    if (name == null) {
        return null;
    }
...
}

You can inspect the name string for the class of the creator. If it's a custom one, you can immediately identify it. Then, if the next reading crashes your app, you can be almost sure that the culprit was the previous read.

Hope it helps.