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