两种序列化方式Serializable和Parcelable
序列化(Serializable):将对象的状态信息转换为可以存储或传输形式的过程。简单来说,序列化就是将运行时的对象状态转换为二进制,然后保存到流,内存或者网络。
在序列化期间,对象将其当前状态写入到临时或持久性存储区,之后,我们可以从存储区中读取或反序列化对象的状态,重新创建该对象。
Serializable是Java提供的序列化接口,它是一个空接口,如下:
public interface Serializable { }
序列化的实现举例:
public class Student implements Serializable { private static final long serialVersionUID = 123456789; private String mName; public String getName() { return mName; } public void setName(String name) { mName = name; } }
Serializable序列化的方式比较简单,除了我们想要实现的内容,只需要添加一个serialVersionUID属性就行,该属性唯一标识一个可序列化的类,在反序列化时用来检查版本号是否一致,不一致会报错:InvalidClassException
Serializable用来标识当前类可以被ObjectOutputStream序列化,可以被ObjectInputStream反序列化
序列化对象:
synchronized public static boolean saveObject(Object obj, String path) { if(null == obj) { return false; } ObjectOutputStream oos = null; try { oos = new ObjectOutputStream(new FileOutputStream(path)); oos.writeObject(obj); oos.close(); return true; }catch(IOException e) { e.printStackTrace(); }finally { if(oos != null) { try { oos.close(); }catch(IOException e) { e.printStackTrace(); } } } return false; }
反序列化对象:
@SuppressWarnings("unchecked ") synchronized public static <T> T readObject(String path) { ObjectInputStream ojs = null; try { ojs = new ObjectInputStream(new FileInputStream(path)); return (T) ojs.readObject(); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } finally { close(ojs); } return null; }
Parcelable是Android特有的序列化接口:
public interface Parcelable { //writeToParcel() 方法中的参数,用于标识当前对象作为返回值返回 //有些实现类可能会在这时释放其中的资源 public static final int PARCELABLE_WRITE_RETURN_VALUE = 0x0001; //writeToParcel() 方法中的第二个参数,它标识父对象会管理内部状态中重复的数据 public static final int PARCELABLE_ELIDE_DUPLICATES = 0x0002; //用于 describeContents() 方法的位掩码,每一位都代表着一种对象类型 public static final int CONTENTS_FILE_DESCRIPTOR = 0x0001; //描述当前 Parcelable 实例的对象类型 //比如说,如果对象中有文件描述符,这个方法就会返回上面的 CONTENTS_FILE_DESCRIPTOR //其他情况会返回一个位掩码 public int describeContents(); //将对象转换成一个 Parcel 对象 //参数中 dest 表示要写入的 Parcel 对象 //flags 表示这个对象将如何写入 public void writeToParcel(Parcel dest, int flags); //实现类必须有一个 Creator 属性,用于反序列化,将 Parcel 对象转换为 Parcelable public interface Creator<T> { public T createFromParcel(Parcel source); public T[] newArray(int size); } //对象创建时提供的一个创建器 public interface ClassLoaderCreator<T> extends Creator<T> { //使用类加载器和之前序列化成的 Parcel 对象反序列化一个对象 public T createFromParcel(Parcel source, ClassLoader loader); } }
实现了 Parcelable 接口的类在序列化和反序列化时会被转换为 Parcel
类型的数据 。
Parcel 是一个载体,它可以包含数据或者对象引用,然后通过 IBinder 在进程间传递。
实现 Parcelable 接口的类必须有一个 CREATOR 类型的静态变量,下面是一个实例:
public class ParcelableGroupBean implements Parcelable { private String mName; private List<String> mMemberNameList; private User mUser; /** * 需要我们手动创建的构造函数 * @param name * @param memberNameList * @param user */ public ParcelableGroupBean(String name, List<String> memberNameList, User user) { mName = name; mMemberNameList = memberNameList; mUser = user; } /** * 1.内容描述 * @return */ @Override public int describeContents() { //几乎都返回 0,除非当前对象中存在文件描述符时为 1 return 0; } /** * 2.序列化 * @param dest * @param flags 0 或者 1 */ @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mName); dest.writeStringList(mMemberNameList); dest.writeParcelable(mUser, flags); } /** * 3.反序列化 */ public static final Creator<ParcelableGroupBean> CREATOR = new Creator<ParcelableGroupBean>() { /** * 反序列创建对象 * @param in * @return */ @Override public ParcelableGroupBean createFromParcel(Parcel in) { return new ParcelableGroupBean(in); } /** * 反序列创建对象数组 * @param size * @return */ @Override public ParcelableGroupBean[] newArray(int size) { return new ParcelableGroupBean[size]; } }; /** * 4.自动创建的的构造器,使用反序列化得到的 Parcel 构造对象 * @param in */ protected ParcelableGroupBean(Parcel in) { mName = in.readString(); mMemberNameList = in.createStringArrayList(); //反序列化时,如果User也是 Parcelable 的类,需要使用它的类加载器作为参数,否则报错无法找到类 mUser = in.readParcelable(User.class.getClassLoader()); } }
可以看到,Serializable 的使用比较简单,创建一个版本号即可;而 Parcelable 则相对复杂一些,会有四个方法需要实现。
一般在保存数据到 SD 卡或者网络传输时建议使用 Serializable 即可,虽然效率差一些,好在使用方便。
而在运行时进行数据传递建议使用 Parcelable,比如 Intent,Bundle 等,Android 底层做了优化处理,效率很高。