Android "java.lang.RuntimeException: Parcelable сталкиваетсяClassNotFoundException, читающий объект Serializable"

У меня есть приложение для Android, в которое я добавляю еще одно перечисление:

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;
    }
}

Я вложил это перечисление и поместил его в пару комплектов намерений для связи с моей основной деятельностью. У меня уже есть еще один enum в моем решении, который не вызывает никаких проблем. Тем не менее, когда я запускаю мое приложение с этим новым enum, которое определяется, оно немедленно сбой:

 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

Я не могу найти корневой случай проблемы. Я также рассмотрел следующие сообщения, без каких-либо результатов:

  • post1 - я не использую загрузчики cusotmclass.
  • post2 - я запускаю приложение непосредственно из ADT, без участия proguard.

Также у меня есть:

  • проверил мои классы в папке bin и там появился необходимый класс.
  • декомпилировал готовый apk с apktool и соответствующий smali тоже там.

ИЗМЕНИТЬ

Теперь я определенно уверен, что исключение фактически вызвано тем, что значение enum в пакете используется для запуска активности, хотя эти строки не упоминаются в stacktrace:

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

Я изменил логику моего приложения, чтобы не использовать это значение перечисления в Bundle только, например. как параметр метода, и теперь он запускается без каких-либо исключений. Кто-нибудь знает, почему это происходит?

Я также ставил щедрость на вопрос сейчас, потому что я более озадачен.

Ответ 1

Просто используйте порядковый номер enum как дополнительный, используя Enum.ordinal(); Кроме того, это должно сделать ваш RootNavigationOption.getValuePosition() устаревшим.

Пример:

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

Позже в вашем MainActivity (или аналогичном):

final int defaultViewModeOrdinal = RootNavigationOption.HOME.ordinal();
final int viewModeOrdinal = getIntent().getIntExtra(Constants.VIEW_MODE, defaultViewModeOrdinal);
final RootNavigationOption viewMode = RootNavigationOption.values()[viewModeOrdinal];

Ответ 2

Я считаю, что проблема заключается в Android с использованием нескольких ClassLoader

Вы можете попробовать установить загрузчик классов до getSerializable()

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

Android E/Parcel: класс не найден при разборке (только на Samsung Tab3)

Вообще-то я предлагаю не использовать сериализуемую версию и реализовать свой собственный parcellable.

Ответ 4

Как я уже говорил в своих комментариях (предполагая, что Constants.VIEW_MODE является строковым ключом):

//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 общественный конструктор не работает согласно документации. Шокирующая. Возможно, мы сможем изменить это использование с помощью этого другого метода: http://developer.android.com/reference/android/os/Bundle.html#setClassLoader(java.lang.ClassLoader)

Попробуйте это и сообщите мне, что происходит:

 Bundle activityOptions = new Bundle(); 
 activityOptions.setClassLoader(RootNavigationOption.class.getClassLoader());
 activityOptions.putSerializable(Constants.VIEW_MODE, RootNavigationOption.HOME);

Ответ 5

В то время как подход ординалов работает, я бы посоветовал более подробно говорить о значениях. Это добавляет немного больше кода для реализации, но делает ваш код более читабельным и явным для защиты от возможных будущих проблем, таких как переупорядочение или вставка новых значений в середине списка.

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);
        }
    }
}

Затем это можно использовать так:

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

// Get 
RootNavigationOption rootNavigationOption = RootNavigationOption.fromCode(bundle.getInt("key"));