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等)。