Android-对象序列化

Serializable

简单使用

Serializable是Java提供的序列化接口,是一个空接口,为对象提供标准的序列化和反序列化操作。

package java.io;

public interface Serializable {
}

为一个对象实现序列化很简单,就是实现此接口即可。

public class SerializableBean implements Serializable {
    private String mString;
    private int mInt;
    private Boolean mBoolean;
    private long mLong;
    private short mShort;
    private byte mByte;
    private char mChar;

    public SerializableBean() {
        mString = "ABC";
        mInt = 21;
        mBoolean = true;
        mLong = 221;
        mShort = 2;
        mByte = (byte) 96;
        mChar = (char) 97;
    }

    public String getString() {
        return mString;
    }

    public void setString(String string) {
        mString = string;
    }

    public int getInt() {
        return mInt;
    }

    public void setInt(int anInt) {
        mInt = anInt;
    }

    public Boolean getBoolean() {
        return mBoolean;
    }

    public void setBoolean(Boolean aBoolean) {
        mBoolean = aBoolean;
    }

    public long getLong() {
        return mLong;
    }

    public void setLong(long aLong) {
        mLong = aLong;
    }

    public short getShort() {
        return mShort;
    }

    public void setShort(short aShort) {
        mShort = aShort;
    }

    public byte getByte() {
        return mByte;
    }

    public void setByte(byte aByte) {
        mByte = aByte;
    }

    public char getChar() {
        return mChar;
    }

    public void setChar(char aChar) {
        mChar = aChar;
    }
    
    @Override
    public String toString() {
        return "SerializableBean{" +
                "mString='" + mString + '\'' +
                ", mInt=" + mInt +
                ", mBoolean=" + mBoolean +
                ", mLong=" + mLong +
                ", mShort=" + mShort +
                ", mByte=" + mByte +
                ", mChar=" + mChar +
                '}';
    }
}

序列化操作,将Object通过ObjectOutputStream写入ByteArrayOutputStream,并转换成字符串储存到SharedPreferences。

// onClick事件触发序列化过程
public void onSerializable(View view) {
    SerializableBean bean = new SerializableBean();
    ByteArrayOutputStream byteArrayOutputStream = null;
    ObjectOutputStream objectOutputStream = null;
    try {
        byteArrayOutputStream = new ByteArrayOutputStream();
        objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(bean);
        objectOutputStream.flush();
        byte[] bytes = Base64.encode(byteArrayOutputStream.toByteArray(), Base64.DEFAULT);
        mSharedPreferences.edit().putString("serializable_result", new String(bytes)).commit();
        mShowSerializableResultText.setText("序列化完成");
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        if (byteArrayOutputStream != null) {
            try {
                byteArrayOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (objectOutputStream != null) {
            try {
                objectOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

反序列化操作,从SharedPreferences读取保存的序列化字符串,通过用ObjectInputStream读取ByteArrayInputStream中的字节反序列化出对象,并打印对象。

// onClick事件触发反序列化过程
public void onDeserializable(View view) {
    String serializableResult = mSharedPreferences.getString("serializable_result", null);
    if (serializableResult != null) {
        ByteArrayInputStream byteArrayInputStream = null;
        ObjectInputStream objectInputStream = null;
        try {
            byte[] resultBytes = Base64.decode(serializableResult.getBytes(), Base64.DEFAULT);
            byteArrayInputStream = new ByteArrayInputStream(resultBytes);
            objectInputStream = new ObjectInputStream(byteArrayInputStream);
            SerializableBean serializableBean = (SerializableBean) objectInputStream.readObject();
            mShowSerializableResultText.setText(serializableBean.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (byteArrayInputStream != null) {
                try {
                    byteArrayInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (objectInputStream != null) {
                try {
                    objectInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

输出如下:

SerializableBean{mString='ABC', mInt=21, mBoolean=true, mLong=221, mShort=2, mByte=96, mChar=a}

可见,一个对象已经被序列化完成。但是在序列化时为什么需要用Base64转换?为什么不直接用ByteArray流生成String保存。刚开始也确实是这样想的,但是结果却不尽人意,当直接把ByteArray转成String写入后再读取String后转ByteArray后,到ObjectInputStream的readObject方法时就出现了错误W/System.err: java.io.StreamCorruptedException: invalid stream header: EFBFBDEF。究其原因就是因为用ObjectOutputStream输出到ByteArray后再转String再转ByteArray的过程出现了字符集错误,将输出的ByteArray进行打印后可以看到头两个字节是0xAC 0xED,转成String后getBytes默认的字符集是按UTF-8来转的,因此转完后再输出时就成了0xEF 0xBF 0xBD 0xEF 0xBF 0xBD,之后将此ByteArray转成对象时就会出现异常。

输出原始ByteArray:

byte[] bytes = byteArrayOutputStream.toByteArray();
StringBuilder stringBuilder = new StringBuilder();
for (byte aByte : bytes) {
    stringBuilder.append(Integer.toHexString(aByte & 0xFF));
    stringBuilder.append(" ");
}
Log.e(TAG, "onSerializable: " + stringBuilder.toString());

输出String的getBytes:

byte[] bytes1 = new String(bytes).getBytes();
stringBuilder = new StringBuilder();
for (byte aByte : bytes1) {
    stringBuilder.append(Integer.toHexString(aByte & 0xFF));
    stringBuilder.append(" ");
}
Log.e(TAG, "onSerializable: " + stringBuilder.toString());

所以需要先将输出的ByteArray用Base64转成规范的不会让UTF-8等字符集产生错误转换的ByteArray,将此ByteArray再转成String就不会有问题了,等readObject时再将字符串转成Base64再转ByteArray。除此之外还可以先转为ISO-8859-1,因为此编码是单字节编码,可以将文件header识别为单字符,将纯二进制转成此编码后再转UTF-8也可以解决问题,相应的readObject时就得逆顺序操作。

容器序列化

  1. List
    需要各元素都得是可序列化的对象,基本类型都是支持序列化的,特别注意的就是元素中是否还有自定义对象不能被序列化。

    for (int i = 0; i < 3; i++) {
        SerializableBean valueBean = new SerializableBean();
        valueBean.setString("Value" + (i + 1));
        mSerializableValueList.add(valueBean);
    }
    
    objectOutputStream.writeObject(mSerializableValueList);
    

    将List序列化后反序列化的结果:

    E/RemoteActivity1: onDeserializable: [
        SerializableBean{mString='Value1', mInt=21, mBoolean=true, mLong=221, mShort=2, mByte=96, mChar=a}, 
        SerializableBean{mString='Value2', mInt=21, mBoolean=true, mLong=221, mShort=2, mByte=96, mChar=a}, 
        SerializableBean{mString='Value3', mInt=21, mBoolean=true, mLong=221, mShort=2, mByte=96, mChar=a}
    ]
    
  2. Map
    比List相同的是Value值是可序列化对象,并且Map的Key值也需要是可序列化对象。

    for (int i = 0; i < 3; i++) {
        SerializableBean valueBean = new SerializableBean();
        valueBean.setString("Value" + (i + 1));
        SerializableBean keyBean = new SerializableBean();
        keyBean.setString("Key" + (i + 1));
        mSerializableBeanMap.put(keyBean, valueBean);
        mSerializableBeanSet.add(valueBean);
    }
    

    将Map序列化后反序列化的结果:

    E/RemoteActivity1: onDeserializable: {
        SerializableBean{mString='Key3', mInt=21, mBoolean=true, mLong=221, mShort=2, mByte=96, mChar=a}=SerializableBean{mString='Value3', mInt=21, mBoolean=true, mLong=221, mShort=2, mByte=96, mChar=a}, 
        SerializableBean{mString='Key1', mInt=21, mBoolean=true, mLong=221, mShort=2, mByte=96, mChar=a}=SerializableBean{mString='Value1', mInt=21, mBoolean=true, mLong=221, mShort=2, mByte=96, mChar=a}, 
        SerializableBean{mString='Key2', mInt=21, mBoolean=true, mLong=221, mShort=2, mByte=96, mChar=a}=SerializableBean{mString='Value2', mInt=21, mBoolean=true, mLong=221, mShort=2, mByte=96, mChar=a}
    }
    
  3. Set
    类似List,只需要元素是可序列化对象。

    for (int i = 0; i < 3; i++) {
        SerializableBean valueBean = new SerializableBean();
        valueBean.setString("Value" + (i + 1));
        mSerializableBeanSet.add(valueBean);
    }
    

    将Set序列化后反序列化的结果:

    E/RemoteActivity1: onDeserializable: [
        SerializableBean{mString='Value3', mInt=21, mBoolean=true, mLong=221, mShort=2, mByte=96, mChar=a}, 
        SerializableBean{mString='Value2', mInt=21, mBoolean=true, mLong=221, mShort=2, mByte=96, mChar=a}, 
        SerializableBean{mString='Value1', mInt=21, mBoolean=true, mLong=221, mShort=2, mByte=96, mChar=a}
    ]
    
  4. 数组
    类似List,只需要元素是可序列化对象。

    private SerializableBean[] mSerializableBeans = new SerializableBean[3];
    
    for (int i = 0; i < 3; i++) {
        SerializableBean valueBean = new SerializableBean();
        valueBean.setString("Value" + (i + 1));
        mSerializableBeans[i] = valueBean;
    }
    

    将数组序列化后反序列化的结果:

    E/RemoteActivity1: onDeserializable: Array 
        SerializableBean{mString='Value1', mInt=21, mBoolean=true, mLong=221, mShort=2, mByte=96, mChar=a}|
        SerializableBean{mString='Value2', mInt=21, mBoolean=true, mLong=221, mShort=2, mByte=96, mChar=a}|
        SerializableBean{mString='Value3', mInt=21, mBoolean=true, mLong=221, mShort=2, mByte=96, mChar=a}|
    

补充

在使用序列化时,可能会碰到这样的情况,已经序列化并且持久化的对象在反序列化时以前的对象已经因为种种原因被更改了,或加属性或减属性等。此时如果再反序列化的话是会出现InvalidClassException

  1. 增属性

    SerializableBean中新增一个属性:

    private String mString1;
    

    当再把之前保存的字符串给反序列化输出的异常如下:

    W/System.err: java.io.InvalidClassException: com.libok.androidnote.bean.SerializableBean; local class incompatible: stream classdesc serialVersionUID = -8862606466551502984, local class serialVersionUID = 6754814338122385435
    

    意思就是从流得到的对象的SerializableID与当前类的serialVersionUID对应不上导致的反序列化失败。在创建对象类并实现Serializable接口时默认是自动生成一个serialVersionUID的,当类做了更改后会生成另一个serialVersionUID,解决办法就是手动指定一个serialVersionUID或者用编辑器去检查生成。

    在AndroidStudio的Setting中搜索serialVersionUID,并开启如下设置:

    在对应的类就会有相应的提示:

    image-20200302215550129

    当有这个serialVersionUID时,再次序列化保存之前的对象,然后再添加属性后反序列化。

    E/RemoteActivity1: onDeserializable: SerializableBean{mString='ABC', mInt=21, mBoolean=true, mLong=221, mShort=2, mByte=96, mChar=a, mString1='null'}
    

    此时就可以正确序列化了,但是因为反序列化的对象并没有mString1属性,所以mString1会直接赋值为null,相应的int->0、long->0、short->0、byte->0、char->0、Boolean->null。以上是增属性,那么删属性会是怎样。

  2. 删属性

    通过打印发现在类中被删除的属性在反序列化的对象中也直接被删除了。

    E/RemoteActivity1: onDeserializable: SerializableBean{mString='ABC', mInt=21, mBoolean=true, mLong=221, mShort=2, mByte=96}
    
  3. 取消实现Serializable接口

    如果取消实现Serializable接口的话,那么强制反序列化是会报异常的。

    W/System.err: java.io.InvalidClassException: com.libok.androidnote.bean.SerializableBean; class invalid for deserialization
    

Parcelable

Parcelable是Android特有的接口,只要实现此接口,一个类的对象就可以实现序列化并可以通过Intent和Binder传递。

简单使用

示例如下:

public class ParcelableBean implements Parcelable {

    private String mString;
    private int mInt;
    private Boolean mBoolean;
    private long mLong;
    private short mShort;
    private byte mByte;
    private char mChar;

    public ParcelableBean() {
        mString = "ABC";
        mInt = 21;
        mBoolean = true;
        mLong = 221;
        mShort = 2;
        mByte = (byte) 96;
        mChar = (char) 97;
    }

    public String getString() {
        return mString;
    }

    public void setString(String string) {
        mString = string;
    }

    public int getInt() {
        return mInt;
    }

    public void setInt(int anInt) {
        mInt = anInt;
    }

    public Boolean getBoolean() {
        return mBoolean;
    }

    public void setBoolean(Boolean aBoolean) {
        mBoolean = aBoolean;
    }

    public long getLong() {
        return mLong;
    }

    public void setLong(long aLong) {
        mLong = aLong;
    }

    public short getShort() {
        return mShort;
    }

    public void setShort(short aShort) {
        mShort = aShort;
    }

    public byte getByte() {
        return mByte;
    }

    public void setByte(byte aByte) {
        mByte = aByte;
    }

    public char getChar() {
        return mChar;
    }

    public void setChar(char aChar) {
        mChar = aChar;
    }

    private ParcelableBean(Parcel in) {
        setString(in.readString());
        setInt(in.readInt());
        setBoolean(in.readInt() == 1);
        setLong(in.readLong());
        setShort((short) in.readInt());
        setByte(in.readByte());
        setChar((char) in.readInt());
    }

    public static final Creator<ParcelableBean> CREATOR = new Creator<ParcelableBean>() {
        @Override
        public ParcelableBean createFromParcel(Parcel in) {
            return new ParcelableBean(in);
        }

        @Override
        public ParcelableBean[] newArray(int size) {
            return new ParcelableBean[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(mString);
        dest.writeInt(mInt);
        dest.writeInt(mBoolean ? 1 : 0);
        dest.writeLong(mLong);
        dest.writeInt(mShort);
        dest.writeByte(mByte);
        dest.writeInt(mChar);
    }

    @Override
    public String toString() {
        return "ParcelableBean{" +
                "mString='" + mString + '\'' +
                ", mInt=" + mInt +
                ", mBoolean=" + mBoolean +
                ", mLong=" + mLong +
                ", mShort=" + mShort +
                ", mByte=" + mByte +
                ", mChar=" + mChar +
                '}';
    }
}
/**
 * Parcelable序列化,随便启动一个Activity并将Parcelable对象放入Intent发送
 */
public void onParcelable(View view) {
    ParcelableBean bean = new ParcelableBean();
    startActivity(new Intent(this, RemoteActivity2.class).putExtra("parcelable_result", bean));
}

/**
 * Parcelable反序列化,在另一个Activity中读取并打印
 */
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_remote2);
    Parcelable parcelableResult = getIntent().getParcelableExtra("parcelable_result");
    if (parcelableResult instanceof ParcelableBean) {
        Log.e(TAG, "onDeparcelable: " + parcelableResult.toString());
    }
}

特别需要注意的是,在writeToParcel方法中的写入顺序必须和ParcelableBean构造函数中的读取顺序保持一致,因为在各种write方法中会写入当前的dataposition,在读取时也是按照dataposition来读取的。而且当写入各种容器时还会将容器长度也一并写入,如果乱序读取的话,是得不到正确结果的。

深入了解

  • Parcelable构造

    Parcel类中包装了可序列化的数据,可以再Binder中自由传输。Parcelable序列化和Serializable不同,不是简单的将类修饰标记下即可的,需要实现对象的序列化写入、反序列化读取以及内容描述。序列化写入是在writeToParcel方法中,通过Parcel中的一系列write方法完成的。反序列化读取是在CREATOR中完成,内部有两个方法分别是反序列化对象和反序列化数组,最终还是通过Parcel中一系列的read方法读取。内容描述是由describeContents方法完成,此方法几乎在所有的情况下都应该返回0,仅当对象中存在文件描述符时需要返回1。

    方法功能标志位
    createFromParcel(Parcel in)从序列化后的对象中创建原始对象
    newArray(int size)创建指定长度的原始对象数组
    ParcelableBean(Parcel in)从序列化后的对象中创建原始对象
    writeToParcel(Parcel dest, int flags)将当前对象写入序列化结构中,其中flags有两种值,0和1。为1时标识当前对象需要作为返回值返回,不能立即释放资源。几乎所有的情况都为0PARCELABLE_WRITE_RETURN_VALUE
    describeContents()返回当前对象的内容描述。如果含有文件描述符,则返回1,否则返回0.几乎所有情况都返回0CONTENTS_FILE_DESCRIPTOR

    系统中提供了很多实现了Parcelable接口的类,都是可以直接序列化的,包括Intent、Bundle、Bitmap等。与Serializable相同的,List、Map也是可以序列化的,同样的需要每个元素都得是可序列化的。

  • 创建数组

    public class ParcelableBean implements Parcelable {
    
        private static final String TAG = "ParcelableBean";
    
        public static final Creator<ParcelableBean> CREATOR = new Creator<ParcelableBean>() {
            @Override
            public ParcelableBean createFromParcel(Parcel in) {
                return new ParcelableBean(in);
            }
    
            @Override
            public ParcelableBean[] newArray(int size) {
                return new ParcelableBean[size];
            }
        };
    
        private String mString;
        private int mInt;
        private Boolean mBoolean;
        private long mLong;
        private short mShort;
        private byte mByte;
        private char mChar;
        private List<ParcelableBean1> mParcelableBean1List = new ArrayList<>();
        private ParcelableBean1[] mParcelableBean1Array = new ParcelableBean1[3];
        private ParcelableBean1[] mParcelableBean1Array1 = new ParcelableBean1[3];
    
        public ParcelableBean() {
            mString = "ABC";
            mInt = 21;
            mBoolean = true;
            mLong = 221;
            mShort = 2;
            mByte = (byte) 96;
            mChar = (char) 97;
            for (int i = 0; i < 3; i++) {
                mParcelableBean1List.add(new ParcelableBean1("TestParcelableList" + (i + 1)));
                mParcelableBean1Array[i] = new ParcelableBean1("TestParcelableArrayObject" + (i + 1));
                mParcelableBean1Array1[i] = new ParcelableBean1("TestParcelableArrayType" + (i + 1));
            }
        }
    
        private ParcelableBean(Parcel in) {
            setString(in.readString());
            setInt(in.readInt());
            setBoolean(in.readInt() == 1);
            setLong(in.readLong());
            setShort((short) in.readInt());
            setByte(in.readByte());
            setChar((char) in.readInt());
    
            in.readTypedList(mParcelableBean1List, ParcelableBean1.CREATOR);
    
            Object[] objects = in.readArray(getClass().getClassLoader());
            if (objects != null) {
                Log.e(TAG, "ParcelableBean: class type " + objects[0].getClass().getSimpleName());
                System.arraycopy(objects, 0, mParcelableBean1Array, 0, objects.length);
            }
    
            in.readTypedArray(mParcelableBean1Array1, ParcelableBean1.CREATOR);
    //        与readTypedArray方法一致,都是读取Parcelable对象数组,只不过readTypedArray是指定一个Array,并且此Array的长度与写入的数组长度一致,否则会报throw new RuntimeException("bad array lengths")异常
    //        而createTypedArray则是根据写入的Parcelable数组返回一个Array,不用担心数组长度问题
    //        通过查看源码发现,此方法调用的是CREATOR中的newArray方法,所以会在参数中需要一个目标对象的CREATOR
    //        mParcelableBean1Array1 = in.createTypedArray(ParcelableBean1.CREATOR);
        }
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(mString);
            dest.writeInt(mInt);
            dest.writeInt(mBoolean ? 1 : 0);
            dest.writeLong(mLong);
            dest.writeInt(mShort);
            dest.writeByte(mByte);
            dest.writeInt(mChar);
            dest.writeTypedList(mParcelableBean1List);
            dest.writeArray(mParcelableBean1Array);
            dest.writeTypedArray(mParcelableBean1Array1, flags);
        }
    
        public String getString() {
            return mString;
        }
    
        public void setString(String string) {
            mString = string;
        }
    
        public int getInt() {
            return mInt;
        }
    
        public void setInt(int anInt) {
            mInt = anInt;
        }
    
        public Boolean getBoolean() {
            return mBoolean;
        }
    
        public void setBoolean(Boolean aBoolean) {
            mBoolean = aBoolean;
        }
    
        public long getLong() {
            return mLong;
        }
    
        public void setLong(long aLong) {
            mLong = aLong;
        }
    
        public short getShort() {
            return mShort;
        }
    
        public void setShort(short aShort) {
            mShort = aShort;
        }
    
        public byte getByte() {
            return mByte;
        }
    
        public void setByte(byte aByte) {
            mByte = aByte;
        }
    
        public char getChar() {
            return mChar;
        }
    
        public void setChar(char aChar) {
            mChar = aChar;
        }
    
        @Override
        public String toString() {
            return "ParcelableBean{" +
                    "mString='" + mString + '\'' +
                    ", mInt=" + mInt +
                    ", mBoolean=" + mBoolean +
                    ", mLong=" + mLong +
                    ", mShort=" + mShort +
                    ", mByte=" + mByte +
                    ", mChar=" + mChar +
                    ", mParcelableBean1List=" + mParcelableBean1List.size() + " " + mParcelableBean1List +
                    ", ArrayObject=" + mParcelableBean1Array[0] +
                    ", " + mParcelableBean1Array[1] +
                    ", " + mParcelableBean1Array[2] +
                    ", ArrayType=" + mParcelableBean1Array1[0] +
                    ", " + mParcelableBean1Array1[1] +
                    ", " + mParcelableBean1Array1[2] +
                    '}';
        }
    }
    
    public class ParcelableBean1 implements Parcelable {
    
        private static final String TAG = "ParcelableBean1";
    
        public static final Creator<ParcelableBean1> CREATOR = new Creator<ParcelableBean1>() {
            @Override
            public ParcelableBean1 createFromParcel(Parcel in) {
                Log.e(TAG, "createFromParcel: ");
                return new ParcelableBean1(in);
            }
    
            @Override
            public ParcelableBean1[] newArray(int size) {
                Log.e(TAG, "newArray: " + size);
                return new ParcelableBean1[size];
            }
        };
    
        private String mString;
        private List<String> mList = new ArrayList<>();
        private Map<String, String> mMap = new HashMap<>();
    
        public ParcelableBean1(String str) {
            mString = str;
            mList.add("ParcelableBean1");
            mMap.put("ParcelableBean1Key", "ParcelableBean1Value");
        }
    
        private ParcelableBean1(Parcel in) {
            in.readStringList(mList);
            in.readMap(mMap, getClass().getClassLoader());
            mString = in.readString();
            Log.e(TAG, "ParcelableBean1: " + mString);
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeStringList(mList);
            dest.writeMap(mMap);
            dest.writeString(mString);
        }
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public String toString() {
            return "ParcelableBean1{" +
                    "mString='" + mString + '\'' +
                    ", mList=" + mList +
                    ", mMap=" + mMap +
                    '}';
        }
    }
    

    简单使用中的ParcelableBean作了一点改动,内部属性除了基本类型再加个Parcelable数组。新建一个ParcelableBean1对象,内部属性为List和Map,都统一测试一下。

    在ParcelableBean中通过writeTypedList方法写入了一个ParcelableList,通过writeArray方法写入了一个ObjectArray,但是元素还都是Parcel,通过writeTypedArray方法写入一个ParcelableArray。Parcel中的方法名中带Typed的就是写入Parcelable对象的方法,在读取时需要传入Parcelable的CREATOR变量。readTypedList方法就是读取写入的ParcelableList,readArray方法就是读取ObjectArray,得注意的是需要传入一个ClassLoader,读取ParcelableArray可以用readTypedArray方法也可以用createTypedArray方法。

    其中createTypedArray方法和readTypedArray方法的区别就是readTypedArray方法需要一个现成的数组,并且该数组的长度必须与写入时数组的长度一致,否则就会报throw new RuntimeException(“bad array lengths”)异常,而createTypedArray方法是不需要事先准备一个数组的,会返回一个新的数组,在创建返回数组时是会调用Parcelable中CREATORnewArray方法,newArray方法只有此一个用处。

    在ParcelableBean1中就简单多了,简单的写入List、Map,简单的读取List和Map,读取Map也是需要提供一个ClassLoader。以下的是传递后的结果:

    E/RemoteActivity2: onDeparcelable: ParcelableBean{
    	mString='ABC', mInt=21, mBoolean=true, mLong=221, mShort=2, mByte=96, mChar=a, 
    	mParcelableBean1List=3 [
    		ParcelableBean1{mString='TestParcelableList1', mList=[ParcelableBean1], mMap={ParcelableBean1Key=ParcelableBean1Value}}, 
    		ParcelableBean1{mString='TestParcelableList2', mList=[ParcelableBean1], mMap={ParcelableBean1Key=ParcelableBean1Value}}, 
    		ParcelableBean1{mString='TestParcelableList3', mList=[ParcelableBean1], mMap={ParcelableBean1Key=ParcelableBean1Value}}
    	], 
    	ArrayObject=
    		ParcelableBean1{mString='TestParcelableArrayObject1', mList=[ParcelableBean1], mMap={ParcelableBean1Key=ParcelableBean1Value}}, 
    		ParcelableBean1{mString='TestParcelableArrayObject2', mList=[ParcelableBean1], mMap={ParcelableBean1Key=ParcelableBean1Value}}, 
    		ParcelableBean1{mString='TestParcelableArrayObject3', mList=[ParcelableBean1], mMap={ParcelableBean1Key=ParcelableBean1Value}}, 
    	ArrayType=
    		ParcelableBean1{mString='TestParcelableArrayType1', mList=[ParcelableBean1], mMap={ParcelableBean1Key=ParcelableBean1Value}}, 
    		ParcelableBean1{mString='TestParcelableArrayType2', mList=[ParcelableBean1], mMap={ParcelableBean1Key=ParcelableBean1Value}}, 
    		ParcelableBean1{mString='TestParcelableArrayType3', mList=[ParcelableBean1], mMap={ParcelableBean1Key=ParcelableBean1Value}}
    }
    
  • 持久化

    Parcelable的持久化与Serializable差不多,都是将ByteArray写入文件,通过读方法说明就可以发现Android是极不推荐将Parcelable持久化的,因为不像Serializable一样如果在指定了SerialVersionID后,反序列化的时候会根据类属性的更改而会适应更改,维护性基本没有,有个属性的增删就会导致读取出问题。此处只是写个示例演示下。

    /**
     * Parcelable序列化
     */
    @SuppressLint("ApplySharedPref")
    public void onParcelable(View view) {
        ParcelableBean bean = new ParcelableBean();
          startActivity(new Intent(this, RemoteActivity2.class).putExtra("parcelable_result", bean));
        byte[] bytes = marshallParcel(bean);
        byte[] encode = Base64.encode(bytes, Base64.DEFAULT);
        mSharedPreferences.edit().putString("parcelable_result", new String(encode)).commit();
    }
    
    public byte[] marshallParcel(Parcelable dest) {
        Parcel parcel = Parcel.obtain();
        parcel.setDataPosition(0);
        dest.writeToParcel(parcel, 0);
        byte[] marshall = parcel.marshall();
        parcel.recycle();
        return marshall;
    }
    
    /**
     * Parcelable反序列化
     */
    public void onDeparcelable(View view) {
        String string = mSharedPreferences.getString("parcelable_result", null);
        if (string != null) {
            Parcelable parcelableResult = unMarshallParcel(Base64.decode(string, Base64.DEFAULT));
            if (parcelableResult instanceof ParcelableBean) {
                Log.e(TAG, "onDeparcelable: " + parcelableResult.toString());
                mShowSerializableResultText.setText(parcelableResult.toString());
            }
        }
    }
    
    public Parcelable unMarshallParcel(byte[] bytes) {
        Log.e(TAG, "unMarshallParcel: " + bytes.length);
        Parcel parcel = Parcel.obtain();
        parcel.unmarshall(bytes, 0, bytes.length);
        parcel.setDataPosition(0);
        Parcelable parcelable = ParcelableBean.CREATOR.createFromParcel(parcel);
        parcel.recycle();
        return parcelable;
    }
    

    持久序列化就是先新获取一个Parcel对象,然后将需要持久化的Parcelable对象写入Parcel,通过marshall方法获得对象的ByteArray,将ByteArray转成String保存或者传输。读取时先将String转成ByteArray,通过新获取的Parcel对象的unmarshall方法把数据写到Parcel对象中,别忘了重置下dataposition,让对象在创建时是从头开始读取序列化数据,之后可以用Parcelable对象的CREATOR中的createFromParcel方法创建相应的对象,此时就相当于普通反序列化的程序步骤了。

总结

Serializable是Java中的序列化接口,使用简单,但是开销大,序列化和反序列化过程都需要大量的IO操作。

Parcelable是Android中特有的序列化方式,使用略显麻烦,但是在Android平台上效率是比Serializable高的,也是Android推荐的序列化方式。但Parcelable将对象序列化到持久存储或者通过网络传输就显得比Serializable麻烦太多。

当主要是在内存中序列化时首选Parcelable,当需要持久化或者网络传输时推荐用Serializable。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值