Android中使用序列化
序列化与反序列化
序列化:将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。
反序列化:序列化的逆向操作,通过从存储区中读取对象的状态,重新创建该对象。
使用目的及场景
使对象持久化
一般情况下,只有当JVM处于运行时,java对象才可能存在,对象的生命周期不会比JVM的生命周期更长。但在现实应用中,存在JVM停止运行后,仍然需要使某些指定的对象存在,并在将来能够重新被读取。这时就需要将对象序列化成另一种方式存储在持久性存储区,如文件、数据库等。
使对象可传输化
使用Java对象时一般通过传递引用来达到传输的目的,但在很多情况下对象的引用由于存储区域的限制无法获取到。比如通过网络传输对象时,比如Android中通过Intent传递对象,通过剪切板传递对象,通过Binder机制跨进程传递对象时,都需要将对象序列化才能传输。
方便维护和扩展
尽管不使用序列化也能完成对象的持久存储和传输,比如程序员可以自己编写代码将对象的字段和属性保存至磁盘,以及从磁盘还原这些字段和属性。但这种方法在随着对象的层次结构变复杂时,需要编写的代码也会变得越来越复杂,在编写包含大量对象的大型业务应用程序的时,程序员不得不为每一个对象编写代码,以便保存字段和属性。序列化则提供了轻松实现这个目标的快捷方法,这样做对程序的维护和扩展有着重要的作用。
常见序列化方式
Serializable、Parcelable序列化
Parcelable和Serializable都可以用于实现序列化,Serializable是Java的实现方式,Parcelable是Android提供的方式。通常在内存上序列化时推荐使用Parcelable,其效率比Serializable高;而在需要存储到设备或者网络传输上时推荐使用Serializable,Parcelable可能会因为Android不同版本的实现不同导致出现问题;
Serializable序列化
使用Serializable非常简单,只需让对应的类实现Serializable接口即可。
package com.test.serializable;
import java.io.Serializable;
/**
* Created by gongw on 2018/9/23.
*/
public class Student implements Serializable{
/**
* serialVersionUID字段在序列化时会写入存储,并在反序列化时
* 取出用于和本地类中的serialVersionUID做校验,如果不一致将
* 抛出java.io.InvalidClassException异常。
* 如果不指定serialVersionUID的值,JVM会自动根据类的结构计
* 算生成一个serialVersionUID,但这样类的任何变化都会导致
* serialVersionUID的变化,从而导致反序列化时失败。所以一般
* 使用时推荐手动指定serialVersionUID的值;
*/
private static final long serialVersionUID = 1L;
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package com.test.serializable;
import java.io.Serializable;
/**
* Created by gongw on 2018/9/23.
*/
public class Student implements Serializable{
/**
* serialVersionUID字段在序列化时会写入存储,并在反序列化时
* 取出用于和本地类中的serialVersionUID做校验,如果不一致将
* 抛出java.io.InvalidClassException异常。
* 如果不指定serialVersionUID的值,JVM会自动根据类的结构计
* 算生成一个serialVersionUID,但这样类的任何变化都会导致
* serialVersionUID的变化,从而导致反序列化时失败。所以一般
* 使用时推荐手动指定serialVersionUID的值;
*/
private static final long serialVersionUID = 1L;
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。
package com.test.serializable;
import java.io.Serializable;
/**
* Created by gongw on 2018/9/23.
*/
public class Student implements Serializable{
/**
* serialVersionUID字段在序列化时会写入存储,并在反序列化时
* 取出用于和本地类中的serialVersionUID做校验,如果不一致将
* 抛出java.io.InvalidClassException异常。
* 如果不指定serialVersionUID的值,JVM会自动根据类的结构计
* 算生成一个serialVersionUID,但这样类的任何变化都会导致
* serialVersionUID的变化,从而导致反序列化时失败。所以一般
* 使用时推荐手动指定serialVersionUID的值;
*/
private static final long serialVersionUID = 1L;
private String name;
//用transient修饰的age变量将不参与序列化
private transient int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package com.test.serializable;
import java.io.Serializable;
/**
* Created by gongw on 2018/9/23.
*/
public class Student implements Serializable{
/**
* serialVersionUID字段在序列化时会写入存储,并在反序列化时
* 取出用于和本地类中的serialVersionUID做校验,如果不一致将
* 抛出java.io.InvalidClassException异常。
* 如果不指定serialVersionUID的值,JVM会自动根据类的结构计
* 算生成一个serialVersionUID,但这样类的任何变化都会导致
* serialVersionUID的变化,从而导致反序列化时失败。所以一般
* 使用时推荐手动指定serialVersionUID的值;
*/
private static final long serialVersionUID = 1L;
private String name;
//用transient修饰的age变量将不参与序列化
private transient int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Parcelable序列化
使用Parcelable时需要让对应的类实现Parcelable接口,并编写describeContents,writeToParcel方法实现,定义CREATOR变量并实现其createFromParcel,newArray 方法,实现带Parcel参数的构造方法。
package com.test.parcelable;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by gongw on 2018/9/23.
*/
public class Student implements Parcelable {
private String name;
private int age;
protected Student(Parcel in) {
name = in.readString();
age = in.readInt();
}
public static final Creator<Student> CREATOR = new Creator<Student>() {
//从Parcel容器中取出数据并进行转换
@Override
public Student createFromParcel(Parcel in) {
return new Student(in);
}
//返回对象数据的大小
@Override
public Student[] newArray(int size) {
return new Student[size];
}
};
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int describeContents() {
return 0;
}
// 将对象数据序列化成一个Parcel对象
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
}
package com.test.parcelable;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by gongw on 2018/9/23.
*/
public class Student implements Parcelable {
private String name;
private int age;
protected Student(Parcel in) {
name = in.readString();
age = in.readInt();
}
public static final Creator<Student> CREATOR = new Creator<Student>() {
//从Parcel容器中取出数据并进行转换
@Override
public Student createFromParcel(Parcel in) {
return new Student(in);
}
//返回对象数据的大小
@Override
public Student[] newArray(int size) {
return new Student[size];
}
};
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int describeContents() {
return 0;
}
// 将对象数据序列化成一个Parcel对象
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
}
Json序列化
Serializable和Parcelable序列化方式除了保存了对象中的属性字段数据外,还据保存了对应的数据类型,而不同平台的数据类型不一样,所以这种方式会受到平台的局限。而在实际开发中存在只需要保存对象的属性字段数据,对数据类型的保真度要求不高,并且要求能够跨平台传输对象数据的场景,这时再使用Serializable和Parcelable就不合适了。
在上述场景中,Json格式序列化在Android开发中用的比较频繁。Json采用文本格式来存储和表示数据,它格式简洁,层次清晰,易于人阅读,也易于机器解析和生成。并且由于其开放标准能够实现跨平台传输,因此广泛应用于Web数据交换。
在Android实现Json序列化有多种方式,可以使用原生的JSONObject, JSONArray。
Student student = new Student();
student.setName("xiaoming");
student.setAge(16);
try{
//将数据存储到Json中
JSONObject object =new JSONObject();
object.put("name", student.getName());
object.put("age", student.getAge());
//从Json中读取数据
String name = object.optString("name");
String age = object.optString("age");
}catch(Exception e){
e.printStackTrace();
}
Student student = new Student();
student.setName("xiaoming");
student.setAge(16);
try{
//将数据存储到Json中
JSONObject object =new JSONObject();
object.put("name", student.getName());
object.put("age", student.getAge());
//从Json中读取数据
String name = object.optString("name");
String age = object.optString("age");
}catch(Exception e){
e.printStackTrace();
}
也可以使用第三方库,如FastJson, Gson, Jackson等。
Student student = new Student();
student.setName("xiaoming");
student.setAge(16);
//使用FastJson将对象序列化为json
String json = JSON.toJSONString(student);
//使用FastJson将json反序列化为对象
Student stu = JSON.parseObject(json, Student.class);
Student student = new Student();
student.setName("xiaoming");
student.setAge(16);
//使用FastJson将对象序列化为json
String json = JSON.toJSONString(student);
//使用FastJson将json反序列化为对象
Student stu = JSON.parseObject(json, Student.class);
总结
- 需要深度拷贝对象时,使用Serializeable和Parcelable。当只在内存传输时,使用Parcelable,在需要存储到设备或者网络传输上时使用Serializable;
- 只需要存储或传输与对象数据类型无关的数据时,需要跨平台传输对象数据时,使用Json(当然还有其他如Xml, Soap等)。