我们知道Intent可以传递基本类型数据,ArrayList、数组、序列化对象(Serilizable和Parcelable);后来发现也可以传递HashMap,因为实现了Serilizable接口。算是惊喜啦。由于HashMap无法保证数据的顺序性,可能会用到LinkedHashMap。使用Intent 传递 LinkedHashMap,传递没有任何问题,但是在读取的时候会出现类型转换异常,HashMap不能转换为LinkedHashMap。傻眼了吧!
Intent传递Map集合
intent.putExtra("map",new LinkedHashMap<>());
或
intent.putExtra("map",new HashMap<>());
Intent读取Map集合
(HashMap)intent.getSerializableExtra("map");
或
(LinkedHashMap)intent.getSerializableExtra("map");
但是获取 LinkedHashMap 时会出现类型转换异常。为什么会这样呢? 通过阅读源码发现,数据处理是在startActivity()里面进行的。
在处理数据时,
Parcel类
public final void writeValue(Object v) {
if (v == null) {
writeInt(VAL_NULL);
} else if (v instanceof String) {
writeInt(VAL_STRING);
writeString((String) v);
} else if (v instanceof Integer) {
writeInt(VAL_INTEGER);
writeInt((Integer) v);
} else if (v instanceof Map) {
writeInt(VAL_MAP);
writeMap((Map) v);
} else if (v instanceof Bundle) {
// Must be before Parcelable
writeInt(VAL_BUNDLE);
writeBundle((Bundle) v);
} else if (v instanceof PersistableBundle) {
writeInt(VAL_PERSISTABLEBUNDLE);
writePersistableBundle((PersistableBundle) v);
} else if (v instanceof Parcelable) {
// IMPOTANT: cases for classes that implement Parcelable must
// come before the Parcelable case, so that their specific
VAL_*
// types will be written.
writeInt(VAL_PARCELABLE);
writeParcelable((Parcelable) v, 0);
} else if (v instanceof Short) {
writeInt(VAL_SHORT);
writeInt(((Short) v).intValue());
} else if (v instanceof Long) {
writeInt(VAL_LONG);
writeLong((Long) v);
} else if (v instanceof Float) {
writeInt(VAL_FLOAT);
writeFloat((Float) v);
} else if (v instanceof Double) {
writeInt(VAL_DOUBLE);
writeDouble((Double) v);
} else if (v instanceof Boolean) {
writeInt(VAL_BOOLEAN);
writeInt((Boolean) v ? 1 : 0);
} else if (v instanceof CharSequence) {
// Must be after String
writeInt(VAL_CHARSEQUENCE);
writeCharSequence((CharSequence) v);
} else if (v instanceof List) {
writeInt(VAL_LIST);
writeList((List) v);
} else if (v instanceof SparseArray) {
writeInt(VAL_SPARSEARRAY);
writeSparseArray((SparseArray) v);
} else if (v instanceof boolean[]) {
writeInt(VAL_BOOLEANARRAY);
writeBooleanArray((boolean[]) v);
} else if (v instanceof byte[]) {
writeInt(VAL_BYTEARRAY);
writeByteArray((byte[]) v);
} else if (v instanceof String[]) {
writeInt(VAL_STRINGARRAY);
writeStringArray((String[]) v);
} else if (v instanceof CharSequence[]) {
// Must be after String[] and before Object[]
writeInt(VAL_CHARSEQUENCEARRAY);
writeCharSequenceArray((CharSequence[]) v);
} else if (v instanceof IBinder) {
writeInt(VAL_IBINDER);
writeStrongBinder((IBinder) v);
} else if (v instanceof Parcelable[]) {
writeInt(VAL_PARCELABLEARRAY);
writeParcelableArray((Parcelable[]) v, 0);
} else if (v instanceof int[]) {
writeInt(VAL_INTARRAY);
writeIntArray((int[]) v);
} else if (v instanceof long[]) {
writeInt(VAL_LONGARRAY);
writeLongArray((long[]) v);
} else if (v instanceof Byte) {
writeInt(VAL_BYTE);
writeInt((Byte) v);
} else if (v instanceof Size) {
writeInt(VAL_SIZE);
writeSize((Size) v);
} else if (v instanceof SizeF) {
writeInt(VAL_SIZEF);
writeSizeF((SizeF) v);
} else if (v instanceof double[]) {
writeInt(VAL_DOUBLEARRAY);
writeDoubleArray((double[]) v);
} else {
Class<?> clazz = v.getClass();
if (clazz.isArray() && clazz.getComponentType() ==
Object.class) {
// Only pure Object[] are written here, Other arrays of
non-primitive types are
// handled by serialization as this does not record the
component type.
writeInt(VAL_OBJECTARRAY);
writeArray((Object[]) v);
} else if (v instanceof Serializable) {
// Must be last
writeInt(VAL_SERIALIZABLE);
writeSerializable((Serializable) v);
} else {
throw new RuntimeException("Parcel: unable to marshal
value " + v);
}
}
}
这里根据类型判断做相应的数据封装。特殊处理了Map类型,而实现Serializable接
口的对象是在else里面处理的。
先看 Map类型的处理方式。
public final void writeMap(Map val) {
writeMapInternal((Map<String, Object>) val);
}
/**
* Flatten a Map into the parcel at the current dataPosition(),
* growing dataCapacity() if needed. The Map keys must be String
objects.
*/
/* package */ void writeMapInternal(Map<String,Object> val) {
if (val == null) {
writeInt(-1);
return;
}
Set<Map.Entry<String,Object>> entries = val.entrySet();
writeInt(entries.size());
for (Map.Entry<String,Object> e : entries) {
writeValue(e.getKey());
writeValue(e.getValue());
}
}
发现,通过遍历Map,分别对键、值写入内存。当然,LinkedHashMap也不例外。
而Serializable类型是写入字节流。
/**
* Write a generic serializable object in to a Parcel. It is strongly
* recommended that this method be avoided, since the serialization
* overhead is extremely large, and this approach will be much slower
than
* using the other approaches to writing data in to a Parcel.
*/
public final void writeSerializable(Serializable s) {
if (s == null) {
writeString(null);
return;
}
String name = s.getClass().getName();
writeString(name);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(s);
oos.close();
writeByteArray(baos.toByteArray());
} catch (IOException ioe) {
throw new RuntimeException("Parcelable encountered " +
"IOException writing serializable object (name = " + name +")", ioe);
}
}
在Intent读取值得时候,
/**
* Read a typed object from a parcel. The given class loader will be
* used to load any enclosed Parcelables. If it is null, the default
class
* loader will be used.
*/
public final Object readValue(ClassLoader loader) {
int type = readInt();
switch (type) {
case VAL_NULL:
return null;
case VAL_STRING:
return readString();
case VAL_INTEGER:
return readInt();
case VAL_MAP:
return readHashMap(loader);
case VAL_PARCELABLE:
return readParcelable(loader);
case VAL_SHORT:
return (short) readInt();
case VAL_LONG:
return readLong();
case VAL_FLOAT:
return readFloat();
case VAL_DOUBLE:
return readDouble();
case VAL_BOOLEAN:
return readInt() == 1;
case VAL_CHARSEQUENCE:
return readCharSequence();
case VAL_LIST:
return readArrayList(loader);
case VAL_BOOLEANARRAY:
return createBooleanArray();
case VAL_BYTEARRAY:
return createByteArray();
case VAL_STRINGARRAY:
return readStringArray();
case VAL_CHARSEQUENCEARRAY:
return readCharSequenceArray();
case VAL_IBINDER:
return readStrongBinder();
case VAL_OBJECTARRAY:
return readArray(loader);
case VAL_INTARRAY:
return createIntArray();
case VAL_LONGARRAY:
return createLongArray();
case VAL_BYTE:
return readByte();
case VAL_SERIALIZABLE:
return readSerializable(loader);
case VAL_PARCELABLEARRAY:
return readParcelableArray(loader);
case VAL_SPARSEARRAY:
return readSparseArray(loader);
case VAL_SPARSEBOOLEANARRAY:
return readSparseBooleanArray();
case VAL_BUNDLE:
return readBundle(loader); // loading will be deferred
case VAL_PERSISTABLEBUNDLE:
return readPersistableBundle(loader);
case VAL_SIZE:
return readSize();
case VAL_SIZEF:
return readSizeF();
case VAL_DOUBLEARRAY:
return createDoubleArray();
default:
int off = dataPosition() - 4;
throw new RuntimeException(
"Parcel " + this + ": Unmarshalling unknown type code " +
type + " at offset " + off);
}
}
map类型调用了readHashMap(loader),Serilizable类型调用了readSerializable
(loader);
public final HashMap readHashMap(ClassLoader loader)
{
int N = readInt();
if (N < 0) {
return null;
}
HashMap m = new HashMap(N);
readMapInternal(m, N, loader);
return m;
}
可以看出,直接new 一个HashMap。并没有创建LinkedHaspMap。这里可以理解为什么会出现类型转换异常了。
/* package */ void readMapInternal(Map outVal, int N,
ClassLoader loader) {
while (N > 0) {
Object key = readValue(loader);
Object value = readValue(loader);
outVal.put(key, value);
N--;
}
}
Map,遍历读取key和value,存储到刚刚新创建的HashMap中。
Serializable,读取字节流反序列化成对象。
private final Serializable readSerializable(final ClassLoader loader) {
String name = readString();
if (name == null) {
// For some reason we were unable to read the name of the
Serializable (either there
// is nothing left in the Parcel to read, or the next value
wasn't a String), so
// return null, which indicates that the name wasn't found in
the parcel.
return null;
}
byte[] serializedData = createByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream
(serializedData);
try {
ObjectInputStream ois = new ObjectInputStream(bais) {
@Override
protected Class<?> resolveClass(ObjectStreamClass osClass)
throws IOException, ClassNotFoundException {
// try the custom classloader if provided
if (loader != null) {
Class<?> c = Class.forName(osClass.getName(),
false, loader);
if (c != null) {
return c;
}
}
return super.resolveClass(osClass);
}
};
return (Serializable) ois.readObject();
} catch (IOException ioe) {
throw new RuntimeException("Parcelable encountered " +
"IOException reading a Serializable object (name = " +
name +
")", ioe);
} catch (ClassNotFoundException cnfe) {
throw new RuntimeException("Parcelable encountered " +
"ClassNotFoundException reading a Serializable object (name = "+ name + ")", cnfe);
}
}
所以,为了保证数据的有序性,一定要传递LinkedHashMap,可以自己创建一个实现 Serializable接口的对象,将LinkedHashMap封装到该对象中,以字节流的方式进行读写。
如:
public class MapIntent implements Serializable {
private LinkedHashMap mMap;
public void setMap(LinkedHashMap map){
mMap=map;
}
public LinkedHashMap getMap() {
return mMap;
}
}