studio getbundle example describecontents android bundle parcelable

getbundle - parcelable android studio



¿Cómo escribir Java.util.Map en parcela de una manera inteligente? (6)

Tengo un mapa genérico de cadenas (clave, valor) y este campo es parte de un Bean que necesito ser parcelable. Por lo tanto, podría usar el método de escritura de parcela # parcela. El documento API dice:

Por favor use writeBundle (Bundle) en su lugar. Aplana un mapa en la parcela en la posición de datos actual (), aumentando la capacidad de datos () si es necesario. Las teclas de mapa deben ser objetos de cadena. Los valores del Mapa se escriben usando writeValue (Objeto) y deben seguir la especificación allí. Se recomienda encarecidamente usar writeBundle (Bundle) en lugar de este método, ya que la clase Bundle proporciona una API de tipo seguro que le permite evitar errores tipográficos misteriosos en el punto de ordenamiento.

Por lo tanto, podría recorrer cada entrada en mi mapa y ponerlo en el paquete, pero sigo buscando una forma más inteligente de hacerlo. ¿Hay algún método en el SDK de Android que me falta?

En el momento lo hago así:

final Bundle bundle = new Bundle(); final Iterator<Entry<String, String>> iter = links.entrySet().iterator(); while(iter.hasNext()) { final Entry<String, String> entry =iter.next(); bundle.putString(entry.getKey(), entry.getValue()); } parcel.writeBundle(bundle);


Buena pregunta. No hay ningún método en la API que conozca que no sea putSerializable y writeMap. La serialización no se recomienda por razones de rendimiento, y writeMap () tampoco se recomienda por razones un tanto misteriosas como ya has señalado.

Necesitaba parcelar un HashMap hoy, así que intenté escribir algunos métodos de utilidad para parcelar el Mapa desde y hacia un Bundle de la forma recomendada:

// Usage: // read map into a HashMap<String,Foo> links = readMap(parcel, Foo.class); // another way that lets you use a different Map implementation links = new SuperDooperMap<String, Foo>; readMap(links, parcel, Foo.class); // write map out writeMap(links, parcel); //////////////////////////////////////////////////////////////////// // Parcel methods /** * Reads a Map from a Parcel that was stored using a String array and a Bundle. * * @param in the Parcel to retrieve the map from * @param type the class used for the value objects in the map, equivalent to V.class before type erasure * @return a map containing the items retrieved from the parcel */ public static <V extends Parcelable> Map<String,V> readMap(Parcel in, Class<? extends V> type) { Map<String,V> map = new HashMap<String,V>(); if(in != null) { String[] keys = in.createStringArray(); Bundle bundle = in.readBundle(type.getClassLoader()); for(String key : keys) map.put(key, type.cast(bundle.getParcelable(key))); } return map; } /** * Reads into an existing Map from a Parcel that was stored using a String array and a Bundle. * * @param map the Map<String,V> that will receive the items from the parcel * @param in the Parcel to retrieve the map from * @param type the class used for the value objects in the map, equivalent to V.class before type erasure */ public static <V extends Parcelable> void readMap(Map<String,V> map, Parcel in, Class<V> type) { if(map != null) { map.clear(); if(in != null) { String[] keys = in.createStringArray(); Bundle bundle = in.readBundle(type.getClassLoader()); for(String key : keys) map.put(key, type.cast(bundle.getParcelable(key))); } } } /** * Writes a Map to a Parcel using a String array and a Bundle. * * @param map the Map<String,V> to store in the parcel * @param out the Parcel to store the map in */ public static void writeMap(Map<String,? extends Parcelable> map, Parcel out) { if(map != null && map.size() > 0) { /* Set<String> keySet = map.keySet(); Bundle b = new Bundle(); for(String key : keySet) b.putParcelable(key, map.get(key)); String[] array = keySet.toArray(new String[keySet.size()]); out.writeStringArray(array); out.writeBundle(b); /*/ // alternative using an entrySet, keeping output data format the same // (if you don''t need to preserve the data format, you might prefer to just write the key-value pairs directly to the parcel) Bundle bundle = new Bundle(); for(Map.Entry<String, ? extends Parcelable> entry : map.entrySet()) { bundle.putParcelable(entry.getKey(), entry.getValue()); } final Set<String> keySet = map.keySet(); final String[] array = keySet.toArray(new String[keySet.size()]); out.writeStringArray(array); out.writeBundle(bundle); /**/ } else { //String[] array = Collections.<String>emptySet().toArray(new String[0]); // you can use a static instance of String[0] here instead out.writeStringArray(new String[0]); out.writeBundle(Bundle.EMPTY); } }

Editar: modifiqué writeMap para usar un entrySet mientras se conserva el mismo formato de datos que en mi respuesta original (que se muestra en el otro lado del comentario de alternar). Si no necesita o no desea conservar la compatibilidad de lectura, puede ser más simple almacenar los pares clave-valor en cada iteración, como en las respuestas de @bcorso y @Anthony Naddeo.


Esta es la mía un tanto simple, pero hasta ahora me está funcionando en Kotlin. Puede modificarse fácilmente si no satisface las necesidades de uno.

Pero no olvide que K,V debe ser Parcelable si es diferente de la String, Int,... habitual String, Int,... etc

Escribir

parcel.writeMap(map)

Leer

parcel.readMap(map)

La lectura superpuesta

fun<K,V> Parcel.readMap(map: MutableMap<K,V>) : MutableMap<K,V>{ val tempMap = LinkedHashMap<Any?,Any?>() readMap(tempMap, map.javaClass.classLoader) tempMap.forEach { map[it.key as K] = it.value as V } /* It populates and returns the map as well (useful for constructor parameters inits)*/ return map }


Si la clave de su mapa es String, puede usar Bundle, como se menciona en javadocs:

/** * Please use {@link #writeBundle} instead. Flattens a Map into the parcel * at the current dataPosition(), * growing dataCapacity() if needed. The Map keys must be String objects. * The Map values are written using {@link #writeValue} and must follow * the specification there. * * <p>It is strongly recommended to use {@link #writeBundle} instead of * this method, since the Bundle class provides a type-safe API that * allows you to avoid mysterious type errors at the point of marshalling. */ public final void writeMap(Map val) { writeMapInternal((Map<String, Object>) val); }

Así que escribí el siguiente código:

private void writeMapAsBundle(Parcel dest, Map<String, Serializable> map) { Bundle bundle = new Bundle(); for (Map.Entry<String, Serializable> entry : map.entrySet()) { bundle.putSerializable(entry.getKey(), entry.getValue()); } dest.writeBundle(bundle); } private void readMapFromBundle(Parcel in, Map<String, Serializable> map, ClassLoader keyClassLoader) { Bundle bundle = in.readBundle(keyClassLoader); for (String key : bundle.keySet()) { map.put(key, bundle.getSerializable(key)); } }

En consecuencia, puede usar Parcelable en lugar de Serializable


Si tanto la key como el value del mapa se extienden a Parcelable , puede tener una solución genérica bastante ingeniosa para esto:

Código

// For writing to a Parcel public <K extends Parcelable,V extends Parcelable> void writeParcelableMap( Parcel parcel, int flags, Map<K, V > map) { parcel.writeInt(map.size()); for(Map.Entry<K, V> e : map.entrySet()){ parcel.writeParcelable(e.getKey(), flags); parcel.writeParcelable(e.getValue(), flags); } } // For reading from a Parcel public <K extends Parcelable,V extends Parcelable> Map<K,V> readParcelableMap( Parcel parcel, Class<K> kClass, Class<V> vClass) { int size = parcel.readInt(); Map<K, V> map = new HashMap<K, V>(size); for(int i = 0; i < size; i++){ map.put(kClass.cast(parcel.readParcelable(kClass.getClassLoader())), vClass.cast(parcel.readParcelable(vClass.getClassLoader()))); } return map; }

Uso

// MyClass1 and MyClass2 must extend Parcelable Map<MyClass1, MyClass2> map; // Writing to a parcel writeParcelableMap(parcel, flags, map); // Reading from a parcel map = readParcelableMap(parcel, MyClass1.class, MyClass2.class);


Terminé haciéndolo un poco diferente. Sigue el patrón que usted esperaría al tratar con Parcelables , por lo que debe ser familiar.

public void writeToParcel(Parcel out, int flags){ out.writeInt(map.size()); for(Map.Entry<String,String> entry : map.entrySet()){ out.writeString(entry.getKey()); out.writeString(entry.getValue()); } } private MyParcelable(Parcel in){ //initialize your map before int size = in.readInt(); for(int i = 0; i < size; i++){ String key = in.readString(); String value = in.readString(); map.put(key,value); } }

En mi aplicación, el orden de las claves en el mapa importaba. Estaba utilizando un LinkedHashMap para preservar el pedido y hacerlo de esta manera garantizaba que las claves aparecerían en el mismo orden después de ser extraídas del Parcel .


puedes probar:

bundle.putSerializable(yourSerializableMap);

Si su mapa elegido se implementa como serializable (como HashMap) y luego puede usar su WriteBundle con facilidad