我有一个字符串的通用映射(键,值),这个字段是我需要可以分配的Bean的一部分。 所以,我可以使用Parcel#writeMap方法。 API Doc说:
请改用writeBundle(Bundle)。将地图展平为包裹 在当前的dataPosition()中,如果需要,增加dataCapacity()。该 映射键必须是String对象。 Map值使用 writeValue(Object)并且必须遵循那里的规范。它是 强烈建议使用writeBundle(Bundle)代替此 方法,因为Bundle类提供了允许的类型安全API 你可以避免在编组时出现神秘的类型错误。
所以,我可以迭代我的Map中的每个Entry并将它放入Bundle中,但我仍然在寻找一种更聪明的方法。我缺少Android SDK中的任何方法吗?
目前我这样做:
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);
答案 0 :(得分:81)
我最后做的有点不同了。它遵循您期望处理Parcelables
的模式,因此它应该是熟悉的。
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);
}
}
在我的应用程序中,地图中键的顺序很重要。我使用LinkedHashMap
来保留排序,这样做可以保证密钥在从Parcel
中提取后以相同的顺序出现。
答案 1 :(得分:25)
你可以尝试:
bundle.putSerializable(yourSerializableMap);
如果您选择的地图实现了可序列化(如HashMap),那么您可以轻松使用writeBundle
答案 2 :(得分:23)
如果地图的key
和value
都延伸Parcelable
,那么您可以使用非常漂亮的Generics解决方案:
// 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;
}
// 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);
答案 3 :(得分:12)
好问题。除了putSerializable和writeMap之外,我所知道的API中没有任何方法。出于性能原因,序列化为not recommended,并且出于某种神秘的原因,不推荐使用writeMap(),正如您已经指出的那样。
我今天需要打包一个HashMap,所以我尝试编写一些实用方法,以推荐的方式将Map分配到Bundle和从Bundle中分配Map:
// 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);
}
}
编辑:修改了writeMap以使用entrySet,同时保留与原始答案相同的数据格式(显示在切换注释的另一侧)。如果您不需要或不想保持读兼容性,那么在每次迭代中存储键值对可能更简单,如@bcorso和@Anthony Naddeo的答案。
答案 4 :(得分:1)
如果你的地图键是String,你可以使用它在javadocs中提到的Bundle:
/**
* 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);
}
所以我写了下面的代码:
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));
}
}
因此,您可以使用Parcelable而不是Serializable
答案 5 :(得分:0)
这是我的一些简单,但到目前为止,我在Kotlin实施。如果它不满足一个需求,它可以很容易地修改
但请不要忘记K,V
如果与通常Parcelable
等不同,则必须String, Int,...
写
parcel.writeMap(map)
读
parcel.readMap(map)
读取重叠
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
}
答案 6 :(得分:0)
这里提到的所有解决方案都是有效的,但没有一个解决方案具有足够的通用性。通常,您有包含字符串,整数,浮点数等值和/或键的映射。在这种情况下,您不能使用<... extended Parcelable>,并且我不想为任何其他键/值组合编写自定义方法。在这种情况下,您可以使用以下代码:
@FunctionalInterface
public interface ParcelWriter<T> {
void writeToParcel(@NonNull final T value,
@NonNull final Parcel parcel, final int flags);
}
@FunctionalInterface
public interface ParcelReader<T> {
T readFromParcel(@NonNull final Parcel parcel);
}
public static <K, V> void writeParcelableMap(
@NonNull final Map<K, V> map,
@NonNull final Parcel parcel,
final int flags,
@NonNull final ParcelWriter<Map.Entry<K, V>> parcelWriter) {
parcel.writeInt(map.size());
for (final Map.Entry<K, V> e : map.entrySet()) {
parcelWriter.writeToParcel(e, parcel, flags);
}
}
public static <K, V> Map<K, V> readParcelableMap(
@NonNull final Parcel parcel,
@NonNull final ParcelReader<Map.Entry<K, V>> parcelReader) {
int size = parcel.readInt();
final Map<K, V> map = new HashMap<>(size);
for (int i = 0; i < size; i++) {
final Map.Entry<K, V> value = parcelReader.readFromParcel(parcel);
map.put(value.getKey(), value.getValue());
}
return map;
}
它比较冗长,但通用。这是写用法:
writeParcelableMap(map, dest, flags, (mapEntry, parcel, __) -> {
parcel.write...; //key from mapEntry
parcel.write...; //value from mapEntry
});
并阅读:
map = readParcelableMap(in, parcel ->
new AbstractMap.SimpleEntry<>(parcel.read... /*key*/, parcel.read... /*value*/)
);