"使用包含ClassLoader作为参数的Parcel.read方法时,解组<myclass>"时出现"BadParcelableException:ClassNotFoundException"

Flo*_*low 49 android marshalling unmarshalling parcel

给定一个自定义类org.example.app.MyClass implements Parcelable,我想写List<MyClass>一个包.我做了编组

 List<MyClass> myclassList = ...
 parcel.writeList(myclassList);
Run Code Online (Sandbox Code Playgroud)

每当我试图解散课程时

 List<MyClass> myclassList = new ArrayList<MyClass>();
 parcel.readList(myclassList, null);
Run Code Online (Sandbox Code Playgroud)

有一个"BadParcelableException: ClassNotFoundException when unmarshalling org.example.app.MyClass"例外.

这有什么不对?为什么找不到课程?

Flo*_*low 103

不要使用null作为ClassLoader参数提供的框架类加载器来解组自定义类(即由应用程序提供而不是由Android框架提供的类).使用应用程序类加载器:

parcel.readList(myclassList, getClass().getClassLoader());
Run Code Online (Sandbox Code Playgroud)

每当一个Parcel.read*()方法也有一个ClassLoader作为参数(例如Parcel.readList(List outVal, ClassLoader loader))并且你想从a读取一个应用程序类时Parcel,使用可以检索的应用程序类加载器getClass().getClassLoader().

背景: Android附带两个类加载器:系统类加载器,它能够加载所有系统类,但是应用程序提供的系统类; 和应用程序类加载器,它将系统类加载器设置为其父类,因此能够加载所有类.如果将null作为类加载器,将使用框架类加载器,从而导致ClassNotFoundException.Parcel.readParcelableCreator()

感谢alexanderblom提供的提示让我走上正轨.

  • 为什么这种情况不会 100% 发生?我在某些用户设备上收到此消息,但无法在我的终端上重现。 (2认同)

Tan*_*ien 18

我相信更正确的形式是:

List<MyClass> myclassList = new ArrayList<MyClass>();
parcel.readList(myclassList, MyClass.class.getClassLoader());
Run Code Online (Sandbox Code Playgroud)

因为在这里您明确使用List的泛型类型的类加载器.

  • 这与我的答案基本相同.`MyClass.class`和`getClass()`之间没有区别,两者都返回相同的类对象,因为在MyClass中调用`getClass()`.此外,您可以在此处使用任何类,只要它是自定义类而不是框架提供的类. (7认同)
  • 我没有说你是不正确的,只是这种形式可能更合适.还要考虑你正在对`getClass()`进行额外的方法调用,这在静态引用类时是不必要的. (7认同)

Was*_*ani 7

我遇到了同样的问题,我使用了 bundle 而不是 parcleable

intent.setExtrasClassLoader(getClassLoader());
/* Send optional extras */
Bundle bundle = new Bundle();
bundle.putParcelable("object", mObject);
bundle.putString("stringVal", stringVal);

intent.putExtra("bundle",bundle);
Run Code Online (Sandbox Code Playgroud)

在我的意图类中,我用它来检索值:

Bundle oldBundle = intent.getBundleExtra("bundle");
ResultReceiver receiver = oldBundle.getParcelable("object");
String stringVal = oldBundle.getString("stringVal");
intent.setExtrasClassLoader(getClassLoader());
Run Code Online (Sandbox Code Playgroud)

希望对一些人有所帮助。


Dav*_*son 5

如果您错误地将自定义视图子类化,您可能会收到此错误。

假设您正在创建子类,BottomNavigationView并且想要将保存的状态添加到onSaveInstanceState().

Parcelable 样板的错误实现(从另一个类或模板复制)如下所示:

static class State extends BaseSavedState {

    Bundle stateBundle;

    //incorrect as super state uses ClassLoaderCreator
    public static final Creator<State> CREATOR = new Creator<State>() {
        public State createFromParcel(Parcel in) {
            return new State(in);
        }

        public State[] newArray(int size) {
            return new State[size];
        }
    };

    State(Parcel source) {
        super(source);
        this.stateBundle = source.readBundle(getClass().getClassLoader());
    }

    State(Parcelable superState) {
        super(superState);
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
        super.writeToParcel(out, flags);
        out.writeBundle(stateBundle);
    }
}
Run Code Online (Sandbox Code Playgroud)

这不会起作用,因为来自的超状态BottomNavigationView需要类加载器。相反,您应该仔细检查SavedStateBottomNavigationView并使用正确的ClassLoaderCreator而不是Creator

static class State extends AbsSavedState {

    Bundle stateBundle;

    public static final Creator<State> CREATOR = new ClassLoaderCreator<State>() {
        public State createFromParcel(Parcel in, ClassLoader classLoader) {
            return new State(in, classLoader);
        }

        @Override
        public State createFromParcel(Parcel source) {
            return new State(source, null);
        }

        public State[] newArray(int size) {
            return new State[size];
        }
    };

    State(Parcel source, ClassLoader classLoader) {
        super(source, classLoader);
        this.stateBundle = source.readBundle(classLoader);
    }

    State(Parcelable superState) {
        super(superState);
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
        super.writeToParcel(out, flags);
        out.writeBundle(stateBundle);
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,扩展android.support.v4.view.AbsSavedState可能是比BaseSavedState或更好的选择,android.view.AbsSavedState因为它允许您将类加载器传递给超类:

SavedState(Parcel source, ClassLoader classLoader) {
    super(source, classLoader); //available in android.support.v4.view.AbsSavedState
    this.stateBundle = source.readBundle(classLoader);
}
Run Code Online (Sandbox Code Playgroud)