首页 > 代码库 > 几种Android数据序列化方案

几种Android数据序列化方案

一、引言

  数据的序列化在Android开发中占据着重要的地位,无论是在进程间通信、本地数据存储又或者是网络数据传输都离不开序列化的支持。而针对不同场景选择合适的序列化方案对于应用的性能有着极大的影响。

  从广义上讲,数据序列化就是将数据结构或者是对象转换成我们可以存储或者传输的数据格式的一个过程,在序列化的过程中,数据结构或者对象将其状态信息写入到临时或者持久性的存储区中,而在对应的反序列化过程中,则可以说是生成的数据被还原成数据结构或对象的过程。

  这样来说,数据序列化相当于是将我们原先的对象序列化概念做出了扩展,在对象序列化和反序列化中,我们熟知的有两种方法,其一是Java语言中提供的Serializable接口,其二是Android提供的Parcelable接口。而在这里,因为我们对这个概念做出了扩展,因此也需要考虑几种专门针对数据结构进行序列化的方法,如现在那些个开放API一般返回的数据都是JSON格式的,又或者是我们Android原生的SQLite数据库来实现数据的本地存储,从广义上来说,这些都可以算做是数据的序列化。

二、Serializable接口

正如前面提到的,Serializable接口是Java语言的特性,是最简单也是使用最广泛的序列化方案之一,这边需要注意的一点是Serializable接口是一个标识接口,无需实现方法,Java便会对这个对象进行序列化操作。

在这里实现了Serializable接口的对象才可以序列化,将Java对象转换成字节序列,而对应的反序列化则是将字节序列恢复成Java对象的过程。

在需要序列化的类中会用到serialVersionUID去标识这个序列化对象,即仅当序列化后的数据中的SerialVersionUID与当前类的serialVersionUID相同时才能被正常的反序列化。

import java.io.*;
public class User implements Serializable{
    private static final long serialVersionUID= 123456;
    public int userId;
    public String userName;
    public boolean isMale;
    public User(int userId,String userName,boolean isMale){
        this.userId=userId;
        this.userName=userName;
        this.isMale = isMale;
    }
    public boolean toSerial(User user) throws IOException{
        ObjectOutputStream out=null;
        boolean status=false;
        try{
            out = new ObjectOutputStream(new FileOutputStream("cache.txt"));
            out.writeObject(user);
            status=true;
            }catch(FileNotFoundException e){
                System.out.println("NO FILE");
            }finally{
                if(out!=null)
                    out.close();
            }
        return status;
    }
    public User toObject(String filename) throws IOException{
        ObjectInputStream in=null;
        boolean status=false;
        User user=null;
        try{
            in = new ObjectInputStream(new FileInputStream(filename));
            user=(User) in.readObject();    
        }catch(ClassNotFoundException e){
            System.out.println("No file");
        }finally{
            if(in!=null)
                in.close();
        }
        return user;
    }
    public static void main(String[] args) throws IOException{
        User user = new User(0,"jake",true);
        System.out.println(user.toSerial(user));
        System.out.println(user.toObject("cache.txt").getClass());
    }
}

此外,需要注意的:静态成员变量是属于类而不属于对象的,所以显然它不会参与到对象的序列化过程中。其次用transient关键字标记的成员变量不参与到序列化过程中。最后,这种序列化方式是基于磁盘或者网络的。

三、Parcelable接口

Parcelable接口是Android API提供的接口,从某种程度上来说,它更适用于Android平台上。不同于Serializable,它是基于内存的,由于内存中读写速度高于磁盘,所以Parcelable接口被广泛用于跨进程对象的传递。

下面贴上一个简单的Parcelable接口的序列化过程:

import android.os.Parcel;
import android.os.Parcelable;
public class User implements Parcelable {
    public int userId;
    public String userName;
    public boolean isMale;
    public User(int userId,String userName,boolean isMale) {
        this.userId=userId;
        this.userName=userName;
        this.isMale=isMale;
    }
    
    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };

    public User(Parcel in) {
        userId=in.readInt();
        userName = in.readString();
        isMale=in.readInt()==1;
    }
    
    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
        out.writeInt(userId);
        out.writeString(userName);
        out.writeInt(isMale?1:0);
    }
}

从上面可以看出,实现一个Parcelable接口,需要实现以下几个方法:

1.构造函数:从序列化后的对象中创建原始对象

2.describeContents :接口内容的描述,一般默认返回0即可

3.writeToParcel:序列化的方法,将类的数据写到parcel容器中

4.静态的parcelable.Creator接口,这个接口包含两个方法

1)createFormParcel:反序列化的方法,将Parcel还原成Java对象

2)newArray:提供给外部类反序列化这个数组使用。

四、两种对象序列化方法的对比

Serializable是Java中的序列化接口,其使用起来简单但开销较大(因为Serializable在序列化过程中使用了反射机制,故而会产生大量的临时变量,从而导致频繁的GC),并且在读写数据过程中,它是通过IO流的形式将数据写入到硬盘或者传输到网络上。

而Parcelable则是以IBinder作为信息载体,在内存上开销比较小,因此在内存之间进行数据传递时,推荐使用Parcelable,而Parcelable对数据进行持久化或者网络传输时操作复杂,一般这个时候推荐使用Serializable。

另外Serializable在使用时比较简单,而Parcelable在使用时需要手动去实现接口中的方法,为了规避使用Parcelable接口时的麻烦,我们下面介绍一个插件,从而自动生成对应的代码。

五、Parcelable插件

为了避免写大量的模板代码,这边介绍一个在Android Strudio中的插件,Android Parcelable code generator。在Pulgins中下载并按照该插件,接下来当我们需要用到Parcelable接口时,该插件就能自动帮我们将类对象转换成实现Parcelable接口的形式。

具体示例如下,

/**
 * Created by DB on 2017/6/24.
 */

public class BookItem {
    public String mName;
    public long mLastTime;
    public String mTitle;
    public String mPath;
}

然后类似与生成getter和setter代码那样,我们就可以直接自动生成Parcelable形式的代码,结果如下所示:

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Created by DB on 2017/6/24.
 */

public class BookItem implements Parcelable {
    public String mName;
    public long mLastTime;
    public String mTitle;
    public String mPath;

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.mName);
        dest.writeLong(this.mLastTime);
        dest.writeString(this.mTitle);
        dest.writeString(this.mPath);
    }

    public BookItem() {
    }

    protected BookItem(Parcel in) {
        this.mName = in.readString();
        this.mLastTime = in.readLong();
        this.mTitle = in.readString();
        this.mPath = in.readString();
    }

    public static final Parcelable.Creator<BookItem> CREATOR = new Parcelable.Creator<BookItem>() {
        @Override
        public BookItem createFromParcel(Parcel source) {
            return new BookItem(source);
        }

        @Override
        public BookItem[] newArray(int size) {
            return new BookItem[size];
        }
    };
}

有了这个插件,使用Parcelable接口显然方便了许多(可以偷好多懒)

六、数据的序列化方案

下面讲到的是广义上的序列化方案,不同于前面两种狭义或者说是对象序列化方案,接下来的几种方案针对于数据的传输和存储过程中的序列化方案

1.SQLite 

SQLite主要用于存储复杂的关系型数据,Android支持原生支持SQLite数据库相关操作(SQLiteOpenHelper),不过由于原生API接口并不友好,所以产生了不少封装了SQLite的ORM框架。

2.SharedPreferences

SharedPreferences是Android平台上提供的一个轻量级存储API,一般用于存储常用的配置信息,其本质是一个键值对存储,支持常用的数据类型如boolean、float、int、long以及String的存储和读取。

使用SharedPreferences读取和存储操作如下:

读取:

1) 获取Sharedpreferences对象

SharedPreferences mPreferences = context.getCSharedPreferences(PREFERENCES_NAME,Context.MODE_PRIVATE);

2.通过SharedPReferences对象读取存储在SharedPreferences中的数据

mPreferences.getBoolean(key,defValue);

存储:

1)获取SharedPreferences.Editor对象

SharedPreferences.Editor editor = mPreferences.edit();

2)通过SharedPreferences.Editor对象写入数据到SharedPreferences中。

mEditor.putBoolean(key,b);

3)调用commit函数将写入的数据提交,从而完成数据存储操作。

mEditor.commit();

3.JSON

JSON是一种轻量级的数据交互格式,由于其相对于XML,体积更小,在网络上传输时更加介绍浏览,被广泛用于移动端。大部分APP与服务端的通信都是使用JSON格式进行交互。

几种Android数据序列化方案