如果我们需要通过Intent和Binder传输数据时就需要使用Parcelable或者Serializable对对象序列化。甚至有时候在把持久化的对象保存在存储设备上或者通过网络传输给其他客户端的时候,需要使用Serializable来完成对象的持久化。在实际开发过程中,习惯了使用序列化传输数据,但是并没有深入了解。Andriod进阶从序列化开始。
private static final long serialVersionUID = 29347521293479523L;
serialVersionUID的工作机制:序列化的时候,系统会把当前类的serialVersionUID写入序列化的文件中( 也可能是其他的中介)。在反序列化的时候,系统会检测文件中的serialVersionUID,是否和当前类的serialVersionUID一致。如果一致就说明序列化的类的版本和当前类的版本相同,此时可以成功反序列化; 否则就说明当前类和序列化的类相比,发生了变化,比如成员的变量数量的增减,或者类型的变更,此时都不能正常的反序列化。
2.transient关键字标记的成员变量不参与序列化过程
从示例代码和效果图可以看出,MainActivity通过Intent向SecondActivity传递一个序列化参数Student,在SecondActivity进行反序列化,并在TextView中显示出来。
通过上述示例,我们可以看出实现Parcelable接口,不仅可以对基本数据类型和对象进行序列化和反序列化,同时能够对List、Map和数组进行序列化和反序列化。在Android系统中,系统对很多类都实现了Parcelable接口,比如Intent、Bundle、BitMap等;
时标识当前对象需要作为返回值,但不能立即释放资源,几乎所有情况都为0;
在Creator接口中包含T createFromParcel(Parcel source)和 T[] newArray(int size)两个方法,它们分别标明了
如何创建序列化对象和数组。
Serializable
简介
Serializable是Java所提供的一个序列化接口,它是一个空接口,为对象提供标准的序列化和反序列化操作。在实际使用时相当的简单,只需要在类的声明中实现Serializable接口即可,同时在类中声明一个标识符即可实现默认的序列化过程。private static final long serialVersionUID = 29347521293479523L;
Serializable的用途
作为一种持久化机制
如果使用的是FileOutputStream流的方式,则数据将被自动地写入文件中,作为一种复制机制
如果使用的是ByteArrayOutputStream流的方式,数据将写入内存中的字节数组中。该字节数组可以用来创建初始对象的副本,作为一种通信机制
如果是使用套接字(Socket)流的方式,则数据自动地通过网络连接传输一另一个端点,并由这个端点上的程序来决定做什么。简单实现
public class User implements Serializable{
private static final long serialVersionUID = 1L;
private int userId;
private String name;
private boolean isMale;
public User(int userId, String name, boolean isMale) {
this.userId = userId;
this.name = name;
this.isMale = isMale;
}
@Override
public String toString() {
return "User{" +
"userId=" + userId +
", name='" + name + '\'' +
", isMale=" + isMale +
'}';
}
}
// 核心代码
User user = new User(1, "Green", true);
ObjectOutputStream out = null;
out = new ObjectOutputStream(new FileOutputStream("cache.text"));
out.writeObject(user);
out.close();
ObjectInputStream in = null;
in = new ObjectInputStream(new FileInputStream("cache.text"));
User userRead = (User) in.readObject();
in.close();
serialVersionUID的必要性
serialVersionUID是用来辅助序列化过程和反序列化过程,原则上序列化后的数据中的serialVersionUID 只有和当前类的serialVersionUID相同的才能正常地反序列化。serialVersionUID的工作机制:序列化的时候,系统会把当前类的serialVersionUID写入序列化的文件中( 也可能是其他的中介)。在反序列化的时候,系统会检测文件中的serialVersionUID,是否和当前类的serialVersionUID一致。如果一致就说明序列化的类的版本和当前类的版本相同,此时可以成功反序列化; 否则就说明当前类和序列化的类相比,发生了变化,比如成员的变量数量的增减,或者类型的变更,此时都不能正常的反序列化。
特别的两点
1.静态成员变量属于类不属于对象,所以不会参与序列化和反序列化过程;2.transient关键字标记的成员变量不参与序列化过程
Parcelable
简介
Parcelable跟Serialable一样,也是一个接口,只要实现这个接口,一个类的对象就可以实现序列化和反序列化,并可以通过Intent或者Binder进行参数传递。代码实现及解析
public class Student implements Parcelable{
private int stuId;
private String name;
private boolean isMale;
private Score score;
private List<Course> listsCource;
private Map<String, Course> mapCource;
private Score[] arryScore;
public Student(int stuId, String name, boolean isMale, Score score, List<Course> listsCource, Map<String, Course> mapCource, Score[] arryScore) {
this.stuId = stuId;
this.name = name;
this.isMale = isMale;
this.score = score;
this.listsCource = listsCource;
this.mapCource = mapCource;
this.arryScore = arryScore;
}
protected Student(Parcel in) {
stuId = in.readInt();
name = in.readString();
isMale = in.readByte() != 0;
score = in.readParcelable(Score.class.getClassLoader());
listsCource = in.createTypedArrayList(Course.CREATOR);
arryScore = in.createTypedArray(Score.CREATOR);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(stuId);
dest.writeString(name);
dest.writeByte((byte) (isMale ? 1 : 0));
dest.writeParcelable(score, flags);
dest.writeTypedList(listsCource);
dest.writeTypedArray(arryScore, flags);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<Student> CREATOR = new Creator<Student>() {
@Override
public Student createFromParcel(Parcel in) {
return new Student(in);
}
@Override
public Student[] newArray(int size) {
return new Student[size];
}
};
@Override
public String toString() {
return "Student{" +
"stuId=" + stuId +
", name='" + name + '\'' +
", isMale=" + isMale +
", score=" + score +
", listsCource=" + listsCource +
", mapCource=" + mapCource +
", arryScore=" + Arrays.toString(arryScore) +
'}';
}
}
public class Score implements Parcelable{
private String cource;
private float score;
public Score(String cource, float score) {
this.cource = cource;
this.score = score;
}
protected Score(Parcel in) {
cource = in.readString();
score = in.readFloat();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(cource);
dest.writeFloat(score);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<Score> CREATOR = new Creator<Score>() {
@Override
public Score createFromParcel(Parcel in) {
return new Score(in);
}
@Override
public Score[] newArray(int size) {
return new Score[size];
}
};
@Override
public String toString() {
return "Score{" +
"cource='" + cource + '\'' +
", score=" + score +
'}';
}
}
public class Course implements Parcelable{
private int code;
private String name;
public Course(int code, String name) {
this.code = code;
this.name = name;
}
protected Course(Parcel in) {
code = in.readInt();
name = in.readString();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(code);
dest.writeString(name);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<Course> CREATOR = new Creator<Course>() {
@Override
public Course createFromParcel(Parcel in) {
return new Course(in);
}
@Override
public Course[] newArray(int size) {
return new Course[size];
}
};
@Override
public String toString() {
return "Course{" +
"code=" + code +
", name=" + name +
'}';
}
}
// MainActivity中
RxView.clicks(btnSerParcelable)
.throttleFirst(1, TimeUnit.SECONDS)
.subscribe(v -> {
Score score = new Score("生物", 99);
List<Course> lists = new ArrayList<>();
lists.add(new Course(10001, "英语"));
lists.add(new Course(10002, "语文"));
lists.add(new Course(10003, "数学"));
Map<String, Course> mapCource = new HashMap<>();
mapCource.put("日语", new Course(10005, "日语"));
mapCource.put("法语", new Course(10006, "法语"));
mapCource.put("德语", new Course(10007, "德语"));
Score[] arryScore = new Score[6];
arryScore[0] = new Score("日语", 99);
arryScore[1] = new Score("法语", 89);
arryScore[2] = new Score("德语", 79);
Student stu = new Student(1132, "张三", false, score, lists, mapCource, arryScore);
Intent intent = new Intent(this, SecondActivity.class);
Bundle bundle = new Bundle();
bundle.putParcelable("Student", stu);
intent.putExtras(bundle);
startActivity(intent);
});
// SecondActivity
Intent intent = getIntent();
Bundle bundle = intent.getExtras();
Student student = bundle.getParcelable("Student");
tv.setText(student.toString());
从示例代码和效果图可以看出,MainActivity通过Intent向SecondActivity传递一个序列化参数Student,在SecondActivity进行反序列化,并在TextView中显示出来。
通过上述示例,我们可以看出实现Parcelable接口,不仅可以对基本数据类型和对象进行序列化和反序列化,同时能够对List、Map和数组进行序列化和反序列化。在Android系统中,系统对很多类都实现了Parcelable接口,比如Intent、Bundle、BitMap等;
public class Intent implements Parcelable, Cloneable {
***
}
public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
***
}
public final class Bitmap implements Parcelable {
***
}
重要方法解析
writeToParcel(Parcel dest, int flags)
此方法实现序列化功能,将当前对象写入序列化结构中,其中flags标识符有两个值:0或者1。为1时标识当前对象需要作为返回值,但不能立即释放资源,几乎所有情况都为0;
int describeContents()
此方法返回当前对象的内容描述,如果含有文件描述符,返回1,否则返回0.几乎所有情况都为0。interface Creator<T> 和 CREATOR
CREATOR作为一个静态内部类实现了反序列化过程,其实CREATOR是接口Creator的匿名实现类。在Creator接口中包含T createFromParcel(Parcel source)和 T[] newArray(int size)两个方法,它们分别标明了
如何创建序列化对象和数组。
T createFromParcel(Parcel source)
此方法从序列化后的对象中创建原始对象T[] newArray(int size)
此方法创建指定长度的原始对象数组构造方法 T(Parcel in)
此方法从序列化后的对象中创建原始对象Serializable和Parcelable优缺点
Serializable是Java中的序列化接口,其使用简单但是开销大,序列化和反序列化过程需要大量的I/O操作。而Parcelable是Android中的序列化方式,既然是google力推的,其更适应于Android平台,尽管其使用麻烦,但是效率高。但是,如果想将对象序列化到设备中或者将对象序列化后通过网络传输,尽管Parcelable也可以实现,但过程复杂,在此种情况下,建议使用Serializable。在Android系统中对性能要求更高,在通过Intent或者Binder传递数据时,建议使用Parcelable,提供效率。参考资料