Android序列化Serializable和Parcelable的理解和区别

一.基本概念

(一)序列化的基本概念

在Android开发过程中,因为无法将对象的引用传给Activities或者Fragments,我们需要将这些对象放到一个Intent或者Bundle里面,然后再传递。

序列化,表示将一个对象转换成可存储或可传输的状态。而序列化后的对象,可以在网络上进行传输,也可以存储到本地。

(二)怎么通过序列化传输对象

Android中Intent如果要传递类对象,可以通过两种方式实现。
1.方式一:Serializable,要传递的类实现Serializable接口传递对象
2.方式二 : Parcelable,要传递的类实现Parcelable接口传递对象。

Serializable(Java自带):
Serializable是序列化的意思,表示将一个对象转换成可存储或可传输的状态。序列化后的对象可以在网络上进行传输,也可以存储到本地。

Parcelable(Android 专用):
除了Serializable之外,使用Parcelable也可以实现相同的效果,
不过不同于将对象进行序列化,Parcelable方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是Intent所支持的数据类型,这样也就实现传递对象的功能了。

二.基本使用

(一)Serializable, 简单易用

我们先看Serializable的代码。弄一个实体类 Person,利用Java自带的Serializable进行序列化

public class Person implements Serializable{

    private static final long serialVersionUID = 7382351359868556980L;
    private String name;
    private int age;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

在数据传递界面:

Intent intent = new Intent(MainActivity.this, SerializableActivity.class);
              
Person person = new Person();
person.setName("如花");
person.setAge(18);
intent.putExtra("put_ser_test", person);
startActivity(intent);

在数据接收界面:

Intent intent = getIntent();
Person per = (Person)intent.getSerializableExtra("put_ser_test");
        
mTvDate.setText("名字:" + per.getName() + "\n"
  + "年龄:" + per.getAge());

Serializable 到此完成,没什么好解释的。Serializable的优点就是简单。

(二)Parcelable, 速度至上

Parcelable在数据传递的使用方面,和Serializable一模一样。在数据的接收方面,唯一的区别在于Serializable是intent.getSerializableExtra,而Parcelable是getIntent().getParcelableExtra。
它们在使用上最大的区别,是实体类:
先看代码:

public class Pen implements Parcelable{

    private String color;
    private int size;


    // 系统自动添加,给createFromParcel里面用
    protected Pen(Parcel in) {
        color = in.readString();
        size = in.readInt();
    }

    public static final Creator CREATOR = new Creator() {

        /**
         *
         * @param in
         * @return
         * createFromParcel()方法中我们要去读取刚才写出的name和age字段,
         * 并创建一个Person对象进行返回,其中color和size都是调用Parcel的readXxx()方法读取到的,
         * 注意这里读取的顺序一定要和刚才写出的顺序完全相同。
         * 读取的工作我们利用一个构造函数帮我们完成了
         */

        @Override
        public Pen createFromParcel(Parcel in) {
            return new Pen(in); // 在构造函数里面完成了 读取 的工作
        }

        //供反序列化本类数组时调用的
        @Override
        public Pen[] newArray(int size) {
            return new Pen[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;  // 内容接口描述,默认返回0即可。
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(color);  // 写出 color
        dest.writeInt(size);  // 写出 size
    }

    // ======分割线,写写get和set
    //个人自己添加
    public Pen() {
    }

    //个人自己添加
    public Pen(String color, int size) {
        this.color = color;
        this.size = size;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

}

当你实现Parcelable接口后,Android Studio(as)就会提示你重写describeContents方法和writeToParcel方法。在writeToParcel方法中,需要写出

dest.writeString(color);  // 写出 color
dest.writeInt(size);  // 写出 size

写完后,发现as依旧提示报错:

这里写图片描述

你根据提示操作后,选择Add Parcelable Implementation,发现as自动添加了以下代码:

public static final Creator CREATOR = new Creator() {

        /**
         *
         * @param in
         * @return
         * createFromParcel()方法中我们要去读取刚才写出的name和age字段,
         * 并创建一个Person对象进行返回,其中color和size都是调用Parcel的readXxx()方法读取到的,
         * 注意这里读取的顺序一定要和刚才写出的顺序完全相同。
         * 读取的工作我们利用一个构造函数帮我们完成了
         */

        @Override
        public Pen createFromParcel(Parcel in) {
            return new Pen(in); // 在构造函数里面完成了 读取 的工作
        }

        //供反序列化本类数组时调用的
        @Override
        public Pen[] newArray(int size) {
            return new Pen[size];
        }
    };

也就是,随便一个类实现了Parcelable接口一开始就会变成这样子。Parcelable方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是Intent所支持的数据类型,这样也就实现传递对象的功能了。

使用Parcelable在代码上的确比Serializable要麻烦一些,但也没有想象中那么麻烦,因为系统已经帮我们做了很多事情。接下来,我们只需要写写我们自己需要的构造方法,写一下私有变量的get和set就行了。

整个实现Parcelable的实体类就完成了。其实,还有更简单的方法.就是使用as的android parcelable code generator插件,一键生成。如何生成的教程,网上有很多。比如这篇[点击跳转]。(http://blog.csdn.net/kroclin/article/details/40902721)

按照教程操作,生成的代码和上述手写的一模一样。如果您是第一次使用android parcelable code generator插件,我简单说一下生成步骤:

1.写出变量

private String color;
private int size;

2.点击鼠标右键:
选择Genetate,然后选择Parcelable就可。截图如下:
Android序列化Serializable和Parcelable的理解和区别_第1张图片

Android序列化Serializable和Parcelable的理解和区别_第2张图片

这里,我们将实现Parcelable接口的方法进行简单说明一下:

方法 功能 标识符
createFromParcel(Parcel source) 从序列化后的对象中创建原始对象
newArray(int size) 创建指定长度的原始对象数组
Pen(Parcel in) 从序列化后的对象中创建原始对象
writeToParcel(Parcel out,int flags) 将当前对象写入序列化结构中 PARCALABLE_WRITE_RETURN_VALUE
describeContents 返回当前对象的内容描述,几乎所有情况都返回0,仅在当前对象中存在文件描述符时返回1 CONTENTS_FILE_DESCRIPTOR

三.原理与性能比较

Serializable的有点在于你只需要对某个类以及它的属性实现Serializable 接口即可。Serializable 接口是一种标识接口(marker interface),这意味着无需实现方法,Java便会对这个对象进行高效的序列化操作。而缺点是:这种方法是使用了反射,序列化的过程较慢。这种机制会在序列化的时候创建许多的临时对象,容易触发垃圾回收。

而Parcelable方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是Intent所支持的数据类型,这样也就实现传递对象的功能了。根据 google 工程师的说法,这些代码将会运行地特别快。原因之一就是我们已经清楚地知道了序列化的过程,而不需要使用反射来推断。同时为了更快地进行序列化,对象的代码也需要高度优化。

那到底Parcelable比Serializable快多少呢?我们来进行测试:

(一)测试方法

  • 通过将一个对象放到一个bundle里面然后调用Bundle#writeToParcel(Parcel, int)方法来模拟传递对象给一个activity的过程,然后再把这个对象取出来。
  • 在一个循环里面运行1000 次。
  • 两种方法分别运行10次来减少内存整理,cpu被其他应用占用等情况的干扰。
  • 参与测试的对象就是上面代码中的Person和 Pen。
  • 在多种Android软硬件环境上进行测试(LG Nexus 4 – Android 4.2.2、Samsung Nexus 10 – Android 4.2.2、HTC Desire Z – Android 2.3.3)

(二)测试结果

测试结果如下图:

Android序列化Serializable和Parcelable的理解和区别_第3张图片
由此可以得出: Parcelable 比 Serializable快了10多倍。有趣的是,即使在Nexus 10这样性能强悍的硬件上,一个相当简单的对象的序列化和反序列化的过程要花将近一毫秒。

四.总结

如果您想让您开发的程序运行更流程,您需要多花点时间来实现 Parcelable ,因为这将会为你对象的序列化过程快10多倍,而且占用较少的资源。加上现在已经有了插件,实现Parcelable接口的实体类代码也可以一键生成,Parcelable 的缺点就显得微不足道了。

区别 Serializable Parcelable
所属API JAVA API Android SDK API
原理 序列化和反序列化过程需要大量的I/O操作 序列化和反序列化过程不需要大量的I/O操作
开销 开销大 开销小
效率 很高
使用场景 序列化到本地或者通过网络传输 内存序列化

源码:
点击下载源码


笔者GitHub地址 点击跳转
您的start就是我分享的最大动力。


参考资料:

  1. 序列化Serializable和Parcelable的理解和区别
  2. Android系统中Parcelable和Serializable的区别

你可能感兴趣的:(Android进程框架)