问题
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.
回答1:
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];
回答2:
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.
回答3:
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.
回答4:
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);
回答5:
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"));
来源:https://stackoverflow.com/questions/32181516/android-java-lang-runtimeexception-parcelable-encounteredclassnotfoundexceptio