Paquete interior parcelable que se agrega a la parcela

En mi proyecto tengo un modelo que contiene información básica sobre el modelo. Por ejemplo, digamos que el modelo es un coche. Luego hay muchas diferentes variedades de coches y estos tienen diferentes datos asignados a ellos. Todos los modelos deben ser parcelables.

Las diferencias entre los diferentes coches es muy pequeña, podría ser sólo unos pocos campos de datos. Así que esto se resuelve mediante la creación de presentadores (sólo una clase que contiene datos) para los diferentes coches. El presentador entonces sabría qué datos adicionales debe contener. Debido a que el presentador en sí no es parcelable tendrá un paquete para todos sus datos que luego la clase de coche, a continuación, añadir a la parcelable. No quiero hacer que los presentadores sean parcelables.

Así que el coche toma el paquete del presentador y lo pone en su paquete:

public void writeToParcel(Parcel parcel, int flags) { parcel.writeBundle(getPresenter().getBundle()); } 

Y luego lo descomprimirá con:

  public Car(Parcel parcel) { getPresenter().setBundle(parcel.readBundle()); } 

Esto funciona bien hasta que un objeto parcelable se agrega al paquete por el presentador. Entonces consigo este error:

 11-16 15:06:37.255: E/AndroidRuntime(15193): FATAL EXCEPTION: main 11-16 15:06:37.255: E/AndroidRuntime(15193): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example/com.example.activity}: android.os.BadParcelableException: ClassNotFoundException when unmarshalling: com.example.model.engine 11-16 15:06:37.255: E/AndroidRuntime(15193): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2185) 11-16 15:06:37.255: E/AndroidRuntime(15193): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2210) 11-16 15:06:37.255: E/AndroidRuntime(15193): at android.app.ActivityThread.access$600(ActivityThread.java:142) 11-16 15:06:37.255: E/AndroidRuntime(15193): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1208) 11-16 15:06:37.255: E/AndroidRuntime(15193): at android.os.Handler.dispatchMessage(Handler.java:99) 11-16 15:06:37.255: E/AndroidRuntime(15193): at android.os.Looper.loop(Looper.java:137) 11-16 15:06:37.255: E/AndroidRuntime(15193): at android.app.ActivityThread.main(ActivityThread.java:4931) 11-16 15:06:37.255: E/AndroidRuntime(15193): at java.lang.reflect.Method.invokeNative(Native Method) 11-16 15:06:37.255: E/AndroidRuntime(15193): at java.lang.reflect.Method.invoke(Method.java:511) 11-16 15:06:37.255: E/AndroidRuntime(15193): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791) 11-16 15:06:37.255: E/AndroidRuntime(15193): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:558) 11-16 15:06:37.255: E/AndroidRuntime(15193): at dalvik.system.NativeStart.main(Native Method) 11-16 15:06:37.255: E/AndroidRuntime(15193): Caused by: android.os.BadParcelableException: ClassNotFoundException when unmarshalling: com.example.model.engine 11-16 15:06:37.255: E/AndroidRuntime(15193): at android.os.Parcel.readParcelable(Parcel.java:2077) 11-16 15:06:37.255: E/AndroidRuntime(15193): at android.os.Parcel.readValue(Parcel.java:1965) 11-16 15:06:37.255: E/AndroidRuntime(15193): at android.os.Parcel.readMapInternal(Parcel.java:2226) 11-16 15:06:37.255: E/AndroidRuntime(15193): at android.os.Bundle.unparcel(Bundle.java:223) 11-16 15:06:37.255: E/AndroidRuntime(15193): at android.os.Bundle.getString(Bundle.java:1055) 11-16 15:06:37.255: E/AndroidRuntime(15193): at com.example.cars.CarPresenter.getExtraString(CarPresenter.java:34) 11-16 15:06:37.255: E/AndroidRuntime(15193): ... 11 more 

Así que de alguna manera no puede leer nada del Bundle.

Esto puede resolverse modificando la llamada readBundle a:

  public Car(Parcel parcel) { getPresenter().setBundle(parcel.readBundle(engine.class.getClassLoader())); } 

Sin embargo, ¿no significaría esto que sólo podría tener un tipo de parcelables en mi paquete? Por ejemplo, ¿qué pasa si otro presentador desea agregar otro objeto parcelable al paquete?

¿Podría alguien arrojar algo de luz sobre esto?

Usted escribió en un comentario:

Sin embargo, mi pregunta era más de por qué tengo que especificar un cargador de clases en este caso

Dianne Hackborn, ingeniero de framework de Android, escribe que:

Cuando el lote lee desde el paquete sólo extrae los datos. En realidad, no es un paquete de datos hasta más tarde cuando se recuperan las cosas del paquete.

En el código fuente Bundle , vemos que el método setClassLoader() establece el campo mClassLoader :

 public void setClassLoader(ClassLoader loader) { mClassLoader = loader; } 

Si no usamos setClassLoader() o el constructor Bundle(ClassLoader loader) , el campo mClassLoader se establecerá en el ClassLoader defecto:

 public Bundle(int capacity) { //... mClassLoader = getClass().getClassLoader(); } 

mClassLoader se utiliza para unmarshal los datos parcelados en el método unparcel() :

  mParcelledData.readMapInternal(mMap, N, mClassLoader); mParcelledData.recycle(); mParcelledData = null; 

Net, si no configura el ClassLoader por defecto al sistema ClassLoader al desmarcar sus datos parcelados, y obtendremos una excepción ClassNotFoundException al unmarshalling nuestros datos personalizados de objeto Parcelable ,

ClassLoaders es una de las características menos comprendidas de Java. Proporcionan "espacios de nombres", y de hecho esos espacios de nombres pueden "anidar" en virtud del hecho de que si una clase no es encontrada por el ClassLoader actual puede comprobar un "padre" ClassLoader.

En el caso de una aplicación de Android hay dos ClassLoaders. Hay uno que sabe cómo cargar clases de su APK y uno que sabe cómo cargar clases desde el marco de Android. El primero tiene este último conjunto como el cargador de clase "Padre" por lo que en general no te das cuenta. Sin embargo, como Bundle es una clase de framework y es cargado por el cargador de clase de framework, necesitas decirle sobre el ClassLoader que se utiliza para encontrar clases en tu APK. El cargador de clases que Bundle utiliza de forma predeterminada es el Framework ClassLoader que es un espacio de nombres "inferior" que el que contiene sus clases APK.

Por lo tanto, diciendo al paquete que ClassLoader utilizar, le está diciendo que necesita comprobar el APK ClassLoader para las clases. Por defecto se comprueba el APK de la estructura, ya que es el ClassLoader el que cargó la clase Bundle y, por lo tanto, es el único "espacio de nombres" en el que se sabe encontrar clases.

Tu escribiste:

Esto puede resolverse modificando la llamada readBundle a:

getPresenter().setBundle(parcel.readBundle(engine.class.getClassLoader())); public Car(Parcel parcel) { getPresenter().setBundle(parcel.readBundle(engine.class.getClassLoader())); }

Sin embargo, ¿no significaría esto que sólo podría tener un tipo de parcelables en mi paquete? Por ejemplo, ¿qué pasa si otro presentador desea agregar otro objeto parcelable al paquete?

En realidad no. Cuando configura el classLoader en engine.class.getClassLoader() , en realidad está proporcionando un cargador de clases que sabe cómo cargar todas las clases de su APK. Así que puede usar el mismo cargador de clases para descomprimir todas las demás clases personalizadas en su aplicación que implementan Parcelable.

El problema que está viendo es que, cuando no especifica un cargador de clases, el cargador de clases del sistema (por defecto) se usa para descompartir el Bundle . El cargador de clases del sistema sólo sabe cómo cargar clases que son conocidas por el sistema Android y no sabe cómo cargar las clases específicas de la aplicación.

Sólo para aclarar, (en general) no hay un "cargador de clase por clase", hay un cargador de clases del sistema y luego hay un "cargador de clases por aplicación". Espero que esto responda tu pregunta.

Este es mi modelo que implementa Parcelable:

 public class ProgramModel implements Parcelable { private int id = -1; private String title = ""; private String time = ""; private String day = ""; private String organization = ""; private String participants = ""; private String description = ""; private String descriptionFull = ""; private String location = ""; private String locationCaption = ""; private int locationCode = 0; private String locationMap = ""; private String imageUrl = ""; private String color = ""; private int colorId = -1; private int columnId = -1; private String startTime = ""; private String endTime = ""; private Date convertedTimeStart = null; private Date convertedTimeEnd = null; private String theme = ""; private ArrayList<TagModel> tags = new ArrayList<TagModel>(); private ArrayList<Integer> tagsId = new ArrayList<Integer>(); public ProgramModel() { } private ProgramModel(Parcel in) { id = in.readInt(); title = in.readString(); time = in.readString(); day = in.readString(); organization = in.readString(); participants = in.readString(); description = in.readString(); descriptionFull = in.readString(); location = in.readString(); locationCaption = in.readString(); locationCode = in.readInt(); locationMap = in.readString(); imageUrl = in.readString(); color = in.readString(); colorId = in.readInt(); columnId = in.readInt(); startTime = in.readString(); endTime = in.readString(); convertedTimeStart = new Date(in.readLong()); convertedTimeEnd = new Date(in.readLong()); theme = in.readString(); in.readTypedList(tags, TagModel.CREATOR); in.readList(tagsId, Integer.class.getClassLoader()); } public int getId() { return id; } public void setId(int id) { this.id = id; } // Other getters and setters @Override public int describeContents() { return 0; } // this is used to regenerate your object public static final Parcelable.Creator<ProgramModel> CREATOR = new Parcelable.Creator<ProgramModel>() { public ProgramModel createFromParcel(Parcel in) { return new ProgramModel(in); } public ProgramModel[] newArray(int size) { return new ProgramModel[size]; } }; @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(id); out.writeString(title); out.writeString(time); out.writeString(day); out.writeString(organization); out.writeString(participants); out.writeString(description); out.writeString(descriptionFull); out.writeString(location); out.writeString(locationCaption); out.writeInt(locationCode); out.writeString(locationMap); out.writeString(imageUrl); out.writeString(color); out.writeInt(colorId); out.writeInt(columnId); out.writeString(startTime); out.writeString(endTime); out.writeLong(convertedTimeStart.getTime()); out.writeLong(convertedTimeEnd.getTime()); out.writeString(theme); out.writeTypedList(tags); out.writeList(tagsId); } } 

Escribir datos en intención en Actividad:

 Intent intent = new Intent(FirstActivity.this, SecondActivity.class); intent.putExtra("ProgramModel", programDetailsModel); startActivity(intent); 

Leer los datos del paquete en Actividad:

 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); savedInstanceState = getIntent().getExtras(); if (savedInstanceState != null) { fromBundleModel = savedInstanceState.getParcelable("ProgramModel"); } } 
  • Falta el campo CREATOR Parcelable?
  • ¿Cómo enviar un objeto de una actividad a otra con Intent.putExtra y Parcel?
  • No se puede pasar el objeto personalizado en un intento: El método Poner extra es ambiguo para el tipo Intención
  • ¿Cómo puedo recuperar de un paquete una secuencia de caracteres que se guardó utilizando TextUtils.writeToParcel (...)?
  • Leer y escribir arreglos de objetos parcelables
  • Limitación de tamaño en adjunto ¿Parcelable?
  • Android Parcelable Implementación con ArrayList <Object>
  • ¿Cómo usar Parcelable en fragmento para obtener datos?
  • ClassNotFoundException cuando unmarshalling y realmente no sé por qué
  • Obtener NullPointerException: Intenta obtener longitud de matriz nula en Parcelable al intentar leer una matriz de bytes en Android
  • ¿Cómo leer y escribir Enum en paquete en Android?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.