parcelize kotlinx example android marshalling unmarshalling parcel

android - kotlinx - "BadParcelableException: ClassNotFoundException al desmarcar<myclass>" al usar el método Parcel.read que tiene un ClassLoader como argumento



parcel android (4)

Dada una clase personalizada, org.example.app.MyClass implements Parcelable , quiero escribir una List<MyClass> en un Parcel. Hice el entrenamiento con

List<MyClass> myclassList = ... parcel.writeList(myclassList);

Cada vez que intento desmarcar toda la clase con

List<MyClass> myclassList = new ArrayList<MyClass>(); parcel.readList(myclassList, null);

hay una "BadParcelableException: ClassNotFoundException when unmarshalling org.example.app.MyClass" .

¿Que esta mal aquí? ¿Por qué no se encuentra la clase?


Creo que una forma más correcta de esto sería:

List<MyClass> myclassList = new ArrayList<MyClass>(); parcel.readList(myclassList, MyClass.class.getClassLoader());

Porque aquí está utilizando explícitamente el cargador de clases para el tipo genérico de la Lista.


Me he enfrentado con el mismo problema y en lugar de parcleable utilicé bundle

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

Y en mi clase de intención utilicé esto para recuperar el valor:

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

Espero que sea de ayuda para algunos.


No desmarque una clase personalizada (es decir, una proporcionada por su aplicación y no por el marco de trabajo de Android) con el cargador de clases de marco que se usa cuando se da un null como el argumento ClassLoader. Utilice el cargador de clases de aplicaciones:

parcel.readList(myclassList, getClass().getClassLoader());

Siempre que un Parcel.read*() también tenga un ClassLoader como argumento (por ejemplo, Parcel.readList(List outVal, ClassLoader loader) ) y desea leer una clase de aplicación de un Parcel , use el cargador de clases de la aplicación que se puede recuperar con getClass().getClassLoader() .

Antecedentes: Android viene con dos cargadores de clase: el cargador de clase de sistema, que puede cargar todas las clases de sistema pero las proporcionadas por su aplicación; y el cargador de clases de la aplicación, que ha establecido el cargador de clases del sistema como principal y, por lo tanto, puede cargar todas las clases. Si Parcel.readParcelableCreator() valor nulo como cargador de clases, Parcel.readParcelableCreator() usará el cargador de clases de marco , lo que provocará una ClassNotFoundException .

Gracias a alexanderblom por proporcionar la pista que me llevó por el camino correcto.


Puede obtener este error si hace una subclase de una vista personalizada de forma incorrecta.

Suponga que está subclasificando BottomNavigationView y desea agregar el estado guardado al superestado en onSaveInstanceState() .

Una implementación incorrecta de la placa de referencia Parcelable (copiada de otra clase o plantilla) se vería así:

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

Esto no funcionaría, ya que el superestado de BottomNavigationView requiere un cargador de clases. En su lugar, debe inspeccionar cuidadosamente la clase SavedState desde BottomNavigationView y usar el ClassLoaderCreator correcto en lugar del 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); } }

Tenga en cuenta que extender android.support.v4.view.AbsSavedState puede ser una mejor opción que BaseSavedState o android.view.AbsSavedState ya que le permitirá pasar un cargador de clases a la superclase:

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