Android “java.lang.RuntimeException: Parcelable en

2019-06-15 14:14发布

I have an Android application in which I add one more enum:

public enum RootNavigationOption {
    HOME(R.string.home, R.drawable.ic_home), SETTINGS(R.string.settings, R.drawable.ic_settings), LOGOUT(
            R.string.logout_label, R.drawable.ic_logout);

    private int navigationOptionLabelResId;
    private int navigationOptionImageResId;

    private RootNavigationOption(int navigationOptionLabelResId, int navigationOptionImageResId) {

        this.navigationOptionLabelResId = navigationOptionLabelResId;
        this.navigationOptionImageResId = navigationOptionImageResId;
    }

    public int getNavigationOptionLabelResId() {
        return navigationOptionLabelResId;
    }

    public int getNavigationOptionImageResId() {
        return navigationOptionImageResId;
    }

    public static int getValuePosition(RootNavigationOption filterOption) {
        int idx = 0;
        for (RootNavigationOption navigationOptionIter : values()) {
            if (navigationOptionIter.equals(filterOption)) {
                return idx;
            }
            idx++;
        }
        return 0;
    }
}

I put in this enum and placed it in couple of intent bundles for communication to my main activity. I already have one more such enum in my solution, which does not cause any problems. However, when I run my application with this new enum being defined, it immediately crashes with:

 java.lang.RuntimeException: Parcelable encounteredClassNotFoundException reading a Serializable object (name = com.pack1.pack2.pack3.helpers.RootNavigationOption)
    at android.os.Parcel.readSerializable(Parcel.java:2219)
    at android.os.Parcel.readValue(Parcel.java:2064)
    at android.os.Parcel.readArrayMapInternal(Parcel.java:2314)
    at android.os.Bundle.unparcel(Bundle.java:249)
    at android.os.Bundle.getString(Bundle.java:1118)
    at android.app.ActivityOptions.<init>(ActivityOptions.java:310)
    at com.android.server.am.ActivityRecord.updateOptionsLocked(ActivityRecord.java:668)
    at com.android.server.am.ActivityStack.startActivityLocked(ActivityStack.java:1778)
    at com.android.server.am.ActivityStackSupervisor.startActivityUncheckedLocked(ActivityStackSupervisor.java:1769)
    at com.android.server.am.ActivityStackSupervisor.startActivityLocked(ActivityStackSupervisor.java:1249)
    at com.android.server.am.ActivityStackSupervisor.startActivityMayWait(ActivityStackSupervisor.java:741)
    at com.android.server.am.ActivityManagerService.startActivityAsUser(ActivityManagerService.java:3118)
    at com.android.server.am.ActivityManagerService.startActivity(ActivityManagerService.java:3104)
    at android.app.ActivityManagerNative.onTransact(ActivityManagerNative.java:135)
    at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2071)
    at android.os.Binder.execTransact(Binder.java:404)
    at dalvik.system.NativeStart.run(Native Method)
 Caused by: java.lang.ClassNotFoundException: com.pack1.pack2.pack3.helpers.RootNavigationOption
    at java.lang.Class.classForName(Native Method)
    at java.lang.Class.forName(Class.java:251)
    at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:2262)
    at java.io.ObjectInputStream.readEnumDescInternal(ObjectInputStream.java:1553)
    at java.io.ObjectInputStream.readEnumDesc(ObjectInputStream.java:1534)
    at java.io.ObjectInputStream.readEnum(ObjectInputStream.java:1579)
    at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:768)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1981)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1938)
    at android.os.Parcel.readSerializable(Parcel.java:2213)
    ... 16 more
 Caused by: java.lang.NoClassDefFoundError: com/pack1/pack2/pack3/helpers/RootNavigationOption
    ... 26 more
 Caused by: java.lang.ClassNotFoundException: Didn't find class "com.pack1.pack2.pack3.helpers.RootNavigationOption" on path: DexPathList[[directory "."],nativeLibraryDirectories=[/system/lib]]
    at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:497)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:457)
    ... 26 more

I am not able to find the root casue of the problem. I also considered the following posts, without any result:

  • post1 - i am not using any cusotmclass loaders.
  • post2 - i am running the application directly from ADT, no proguard involved.

Also I have:

  • checked my classes in the bin folder and the needed class is in there.
  • decompiled the ready apk with apktool and the respective smali is in there too

EDIT

Now I am definitely sure that the exception is actually caused by putting the enum value in a bundle used to start the activity, although these lines are not mentioned in the stacktrace:

Bundle activityOptions = new Bundle();
activityOptions.putSerializable(Constants.VIEW_MODE, RootNavigationOption.HOME);
Intent intent = new Intent(this, MainActivity.class);

I have changed the logic of my application to not use this enum value in Bundle only e.g. as method parameter and now it runs without any exceptions. Does anyone have a clue why this happens?

I also place a bounty on the question now, because I am more perplexed.

5条回答
叛逆
2楼-- · 2019-06-15 14:47

I believe the problem lies on Android using multiple ClassLoader

You can try setting the class loader before getSerializable()

bundle.setClassLoader(getClass().getClassLoader());

Android E/Parcel﹕ Class not found when unmarshalling (only on Samsung Tab3)

Actually I would suggest not to use serializable, and implements your own parcellable.

查看更多
Lonely孤独者°
3楼-- · 2019-06-15 14:49

As I suggested in my comments (assuming that Constants.VIEW_MODE is a String key):

//Inside an activity or use getApplicationConext().getClassLoader()
ClassLoader loader = this.getClassLoader(); 
Bundle activityOptions = new Bundle(loader);
activityOptions.putSerializable(Constants.VIEW_MODE, RootNavigationOption.HOME);

EDIT:

Hmmm..so the public constructor is not working as per documentation. Shocking. Maybe we can force a change using this another method: http://developer.android.com/reference/android/os/Bundle.html#setClassLoader(java.lang.ClassLoader)

Try this instead and let me know what happens:

 Bundle activityOptions = new Bundle(); 
 activityOptions.setClassLoader(RootNavigationOption.class.getClassLoader());
 activityOptions.putSerializable(Constants.VIEW_MODE, RootNavigationOption.HOME);
查看更多
一夜七次
4楼-- · 2019-06-15 15:05

Looks like Android is unbundling on another process onto which the intent is passed and tried to be processed, where your enum doesn't live; read over: Passing enum or object through an intent (the best solution) for more information.

查看更多
疯言疯语
5楼-- · 2019-06-15 15:06

Simply use the enum ordinal as extra using Enum.ordinal(); additionally, this should make your RootNavigationOption.getValuePosition() obsolete.

Example:

final Intent intent = new Intent(this, MainActivity.class);
intent.putExtra(Constants.VIEW_MODE, RootNavigationOption.SETTINGS.ordinal());

Later in your MainActivity (or similar):

final int defaultViewModeOrdinal = RootNavigationOption.HOME.ordinal();
final int viewModeOrdinal = getIntent().getIntExtra(Constants.VIEW_MODE, defaultViewModeOrdinal);
final RootNavigationOption viewMode = RootNavigationOption.values()[viewModeOrdinal];
查看更多
闹够了就滚
6楼-- · 2019-06-15 15:13

While the ordinals approach works, I would advise being more explicit about the values. This does add a bit more code to implement, but it makes your code more readable and explicit to protect against possible future issues like reordering or inserting new values in the middle of the list.

public enum RootNavigationOption {
    HOME(0, R.string.home, R.drawable.ic_home),
    SETTINGS(1, R.string.settings, R.drawable.ic_settings),
    LOGOUT(2, R.string.logout_label, R.drawable.ic_logout);

    // Note the added code instance variable here, used in the constructor as well.
    private final int code;
    private final int navigationOptionLabelResId;
    private final int navigationOptionImageResId;

    private RootNavigationOption(int code,
                                 int navigationOptionLabelResId,
                                 int navigationOptionImageResId) {
        this.code = code;
        this.navigationOptionLabelResId = navigationOptionLabelResId;
        this.navigationOptionImageResId = navigationOptionImageResId;
    }

    public int getNavigationOptionLabelResId() {
        return navigationOptionLabelResId;
    }

    public int getNavigationOptionImageResId() {
        return navigationOptionImageResId;
    }

    public int getCode() {
        return code;
    }

    public static RootNavigationOption fromCode(int code) {
        switch(code) {
            case 0:
                return HOME;
            case 1:
                return SETTINGS;
            case 2:
                return LOGOUT;
            default:
                throw new RuntimeException(
                    "Illegal RootNavigationOption: " + code);
        }
    }
}

This can then be used like so:

// Put
Bundle bundle = new Bundle();
bundle.putInt("key", RootNavigationOption.HOME.getCode());

// Get 
RootNavigationOption rootNavigationOption = RootNavigationOption.fromCode(bundle.getInt("key"));
查看更多
登录 后发表回答