【问题标题】:Android "java.lang.RuntimeException: Parcelable encounteredClassNotFoundException reading a Serializable object"Android“java.lang.RuntimeException:Parcelable遇到ClassNotFoundException读取可序列化对象”
【发布时间】:2015-11-17 19:55:39
【问题描述】:

我有一个 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 文件夹中的课程,并且需要的课程在那里。
  • 用apktool反编译准备好的apk,相应的smali也在里面

编辑

现在我可以确定异常实际上是由于将枚举值放入用于启动 Activity 的包中引起的,尽管堆栈跟踪中没有提到这些行:

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

我已经更改了我的应用程序的逻辑,以不在 Bundle 中使用这个枚举值,例如作为方法参数,现在它运行没有任何异常。有谁知道为什么会发生这种情况?

我现在也悬赏这个问题,因为我更困惑。

【问题讨论】:

  • on path: DexPathList[[directory "."],nativeLibraryDirectories=[/system/lib]] 但是你说你的类存储在bin 文件夹中?
  • 这很奇怪,根据 Enum 的文档,它说它是可序列化的。 developer.android.com/reference/java/lang/Enum.html
  • @JoxTraex 不仅如此——我成功地在 Bundle 中使用了另一个枚举(但它是片段的参数)
  • 嗯,大小有区别吗?会不会跟那个有关?
  • @BNK 我使用 ADT 进行开发,是的,这些类位于项目的 bin 文件夹中。但是,我可能遗漏了您在评论的路径部分所指的内容。

标签: java android android-intent enums classnotfoundexception


【解决方案1】:

使用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 正在将意图传递到另一个进程上并试图对其进行处理,而您的枚举并不存在;阅读:Passing enum or object through an intent (the best solution) 了解更多信息。

    【讨论】:

    • 感谢您的链接。你能推测一下解绑的另一个过程吗?
    • 您从包含导致问题的捆绑包的意图开始是什么?
    • 我正在开始一项活动
    • 同一进程中的一个活动?什么样的活动——我的意思是:你能想到任何原因,Android 会为正在启动的任何活动启动一个单独的进程吗?
    • 启动器活动以此意图启动该行中的第二个活动。我认为没有理由进行特殊处理
    【解决方案3】:

    我认为问题出在使用多个 ClassLoader 的 Android 上

    你可以尝试在getSerializable()之前设置类加载器

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

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

    其实我建议不要使用serializable,而是自己实现parcellable。

    【讨论】:

      【解决方案4】:

      正如我在 cmets 中建议的那样(假设 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);
      

      编辑:

      嗯..所以公共构造函数没有按照文档工作。 令人震惊。 也许我们可以使用另一种方法强制更改: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);
      

      【讨论】:

      • 感谢您的建议,但问题仍然存在。
      • @BorisStrandjev 看到我编辑的答案,我拒绝投降:)
      • 还是一样的结果,
      • 绝对是一个类加载器问题,但我已经达到了我的极限。我的最后一次尝试,以更 Java 的方式强制 ClassLoader:activityOptions.setClassLoader(Thread.currentThread().getContextClassLoader());
      【解决方案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"));
      

      【讨论】:

        猜你喜欢
        • 2015-06-15
        • 2011-06-19
        • 2019-07-21
        • 2011-08-26
        • 2013-08-11
        • 1970-01-01
        • 2016-01-25
        • 2014-06-02
        相关资源
        最近更新 更多