IPC通信基础介绍之Serializable和Parcelable详解
都是用来序列化的,什么时候用到序列化呢?
1、把对象持久化到存储设备上,保存对象的字节序列到本地文件中。
2、通过网络传输给其他客户端。
3、通过Intent和Binder传输数据。
Serializable接口
serializable是java提供的一个序列化接口,它是一个空接口,使用特别简单,只需要实现Serializable接口即可。
在实现Serializable接口的同时,会有一个可有可无的serialVersionUID参数,这个参数可有可无是因为它对我们正常序列化是没有影响的,它只对反序列化产生影响,稍后说明对反序列化的影响。
private static final long serialVersionUID = 871136744783885433L
通过Serializable实现的序列化,几乎所有的工作都被系统自动完成了。
public class User implements Serializable {
private static final long serialVersionUID = 519067123721295773L;
private int userId;
private String name;
private boolean isMale;
}
针对序列化和反序列化过程其实可以通过ObjectOutputStream和ObjectInputStream实现。
//序列化过程
User user = new User(0, true, "aaa");
try {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("user.txt"));
out.writeObject(user);
out.close();
} catch (IOException e) {
e.printStackTrace();
}
//反序列化过程
try {
ObjectInputStream in = new ObjectInputStream(new FileInputStream("user.txt"));
User newUser = (User) in.readObject();
in.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
反序列化后的对象newUser和user的内容完全一样,但是他们并不是同一个对象。
对于参数serialVersionUID,它既可以手动指定,也可以系统自动生成。它的工作机制是这样的,在当前类进行序列化的过程中serialVersionUID会被写入序列化文件中,当这个类进行反序列化的时候系统会去检测文件中的serialVersionUID和当前类中的是否一致,如果一致说明当时被序列化的类的版本和当前类的版本是相同的,那么就可以成功实现反序列化;如果不一致,就说明当前类发生了变化,例如成员变量数量、类型等发生了改变,这个时候反序列化就会失败。
对于serialVersionUID的手动生成和系统自动生成区别在于,如果类中的成员变量在数量和类型上发生了改变,那么手动生成的serialVersionUID不会改变,也就不会反序列化失败;反之,系统是根据类的结构自动生成的hash值,如果成员变量发生了改变,那么对应的hash值也会发生改变,就会造成当前成员变量已经发生改变的类的serialVersionUID和之前序列化保存在文件中的serialVersionUID对应不起来,从而反序列化失败。
注意:静态的成员变量属于类不属于对象,所以不会参与序列化过程;还有是transient关键字标记的成员变量不参与序列化过程。
Parcelable接口
Parcelable是Android提供的一个接口,实现这个接口就可以实现序列化对象。
一个实现Parcelable的子类Child:
public class Child implements Parcelable {
public String id;
public String name;
public boolean isMale;
protected Child(Parcel in) {
id = in.readString();
name = in.readString();
isMale = in.readByte() != 0;
}
public static final Creator<Child> CREATOR = new Creator<Child>() {
@Override
public Child createFromParcel(Parcel in) {
return new Child(in);
}
@Override
public Child[] newArray(int size) {
return new Child[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeString(name);
dest.writeByte((byte) (isMale ? 1 : 0));
}
}
一个含有Child的成员变量的父类Parent:
public class Parent implements Parcelable {
public String id;
public String name;
public boolean isMale;
public Child child;
/**
* 实现反序列化功能,通过一系列read方法完成
*
* @param in
*/
protected Parent(Parcel in) {
id = in.readString();
name = in.readString();
isMale = in.readByte() != 0;
child = in.readParcelable(Child.class.getClassLoader());
}
/**
* 完成反序列化
*/
public static final Creator<Parent> CREATOR = new Creator<Parent>() {
/**
* 从序列化后的对象中创建原始对象
* @param in
* @return
*/
@Override
public Parent createFromParcel(Parcel in) {
return new Parent(in);
}
/**
* 创建指定长度的原始对象数组
* @param size
* @return
*/
@Override
public Parent[] newArray(int size) {
return new Parent[size];
}
};
/**
* 内容描述功能
* 含有文件描述返回1,否则返回0,几乎所有情况都返回0
*
* @return
*/
@Override
public int describeContents() {
return 0;
}
/**
* 将当前对象写入序列化结构中
* 是通过一些列的wtire方法完成的
*
* @param dest
* @param flags
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeString(name);
dest.writeByte((byte) (isMale ? 1 : 0));
dest.writeParcelable(child, flags);
}
}
在以上代码中序列化功能由wtireToParcel方法完成,反序列化是由CREATOR完成。在Parent中的成员变量Child,Child也必须是实现Parcelable可序列化的。
对于实现序列化对于两则的选取是各有优缺点的。Serializable是java中的序列化接口,其序列化过程需要大量的I/O操作,性能消耗比较大。而Parcelable是Android中提供的,因此可能更加适合在安卓开发中使用,它的缺点就是使用比较麻烦,但是效率比较高。至于选择哪个个人感觉还是看实际项目复杂情况吧。
。