Android Intent传递LinkedHashMap

我们知道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;
  }
}








展开阅读全文

没有更多推荐了,返回首页