美文网首页
Serializable与Parcelable的区别

Serializable与Parcelable的区别

作者: snowyeti | 来源:发表于2021-01-07 20:44 被阅读0次

今天刚好看到了Intent的源码,发现它实现了Parcelable接口,突然想起了Java里面还有一个Serializable接口,于是想借此机会谈谈自己对这两个接口的看法。
在安卓里面,我们都知道无法直接把对象传给activity,为了传递对象,这些对象要么必须实现Serializable接口,要么必须实现Parcelable接口。那到底这两者有什么区别呢?我们先来看看 Serializable 接口。

1.Serializable
先看下Java源码中 Serializable 接口的定义:

package java.io;
public interface Serializable {
}

Serializable 是一个标准的Java接口,从接口定义中我们看到,它位于 java.io 包目录下,没有定义任何方法和变量,只用于确定可序列化的语义。因此我们可以实现这个接口并自己添加方法。但是序列化机制使用了反射去构造序列化对象,因此性能较慢。而且它创建了大量的临时对象,从而造成了相当大的GC,这个接口呢它比较容易实现,我们来看个例子:

class SeObject implements Serializable {

    private String name;
    private int age;

    public SeObject(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

在这里我定义了一个 SeObject,实现了此接口,并定义了一个 name 和 age,实现了构造函数和 getter 方法。打算在activity之间传递这个 SeObject 对象,再来看看第一个activity是如何传递 SeObject 对象的:

    public void startActivity(View view) {
        Intent intent = new Intent(this, SecondActivity.class);
        SeObject seObject = new SeObject("Leon", 28);
        Bundle bundle = new Bundle();
        bundle.putSerializable("Object", seObject);
        intent.putExtras(bundle);
        startActivity(intent);
    }

首先,在第一个activity的布局里面定义了一个Button,设置 onClick 属性为 startActivity,这样的话点击Button时就会调用上面这个方法。可以看到,我是先定义了一个Bundle,将 SeObject 对象传入 putSerializable 方法,并将 bundle 对象放入 Intent,传递到 SecondActivity,来看看 SecondActivity.java 是如何获取这个 SeObject 对象的:

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        Intent intent = getIntent();
        Bundle bundle = intent.getExtras();
        SeObject seObject = (SeObject) bundle.getSerializable("Object");
        Log.d("SecondActivity", "Name is " + seObject.getName() + ", age is " +         
             seObject.getAge());
    }

打印的LOG如下:

D/SecondActivity( 7760): Name is Leon, age is 28

大家仔细对比下这两个文件的代码,很细心的同学应该会发现有一个对应关系:

putExtras -> getExtras;putSerializable -> getSerializable

在获取 SeObject 对象时,先调用 intent.getExtras() 拿到 Bundle对象,再调用 bundle.getSerializable从 bundle 对象中读取 SeObject对象,跟第一个activity中放入 SeObject 对象的顺序恰好相反,大家记住这个顺序,以后在Intent中传递自定义对象就很容易实现了,也不容易搞错。但经过自己验证,下面这种方式也能获取到 SeObject 对象:

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        Intent intent = getIntent();
        SeObject seObject = (SeObject) intent.getSerializableExtra("Object");
        Log.d("SecondActivity", "11 Name is " + seObject.getName() + ", age is " + 
             seObject.getAge());
    }

(但是我个人还是喜欢第一种实现方式,不容易搞错,尤其是传入多个对象时,大家根据自己的习惯来就好)。

我们再来看看 Parcelable 接口。
2.Parcelable
Parcelable 接口的定义:

package android.os;
public interface Parcelable {
    public void writeToParcel(Parcel dest, @WriteFlags int flags);
    public static final int PARCELABLE_WRITE_RETURN_VALUE = 0x0001;
    public @ContentsFlags int describeContents();
    public interface Creator<T> {
        /**
         * Create a new instance of the Parcelable class, instantiating it
         * from the given Parcel whose data had previously been written by
         * {@link Parcelable#writeToParcel Parcelable.writeToParcel()}.
         * 
         * @param source The Parcel to read the object's data from.
         * @return Returns a new instance of the Parcelable class.
         */
        public T createFromParcel(Parcel source);

        /**
         * Create a new array of the Parcelable class.
         * 
         * @param size Size of the array.
         * @return Returns an array of the Parcelable class, with every entry
         * initialized to null.
         */
        public T[] newArray(int size);
    }

我在这里只列出了一部分源码,从源码可以看出,Parcelable位于 android.os 包目录下,它不仅定义了一些变量和方法,还定义了一个内部接口 Creator<T>。Parcelable 过程比序列化过程快得多,其中一个原因是我们明确了序列化过程而不是使用反射来推断它,代码为此进行了大量优化。我们来看一个 Parceable 的例子:

class ParObject implements Parcelable {

    private String name;
    private int age;

    public ParObject(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public ParObject(Parcel source) {
        name = source.readString();
        age = source.readInt();
    }

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(age);
    }

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }

    public static final Creator<ParObject> CREATOR = new Creator<ParObject>() {
        @Override
        public ParObject[] newArray(int size) {
            return new ParObject[size];
        }

        @Override
        public ParObject createFromParcel(Parcel source) {
            return new ParObject(source);
        }
    };

}

Android 的 Parcelable 实现允许从 Parcel 中读取和写入,Parcel 在消息容器中包含了扁平的数据。如果我们想把Java对象转化成Parcelable,最好的方式就像上面那样实现Parcelable接口,并复写 writeToParcel 方法。第一步就是复写 writeToParcel 方法,将对象的所有成员写进 parcel 对象;第二步是创建一个 static 的 Parcelable.Creator 对象去实现反序列化。

public void startActivity(View view) {    
        ParObject parObject = new ParObject("Leon", 27);
        Intent mIntent = new Intent(this, SecondActivity.class);
        Bundle bundle = new Bundle();
        bundle.putParcelable("object", parObject);
        mIntent.putExtras(bundle);
        startActivity(mIntent);
    }
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        Intent intent = getIntent();
        Bundle bundle = intent.getExtras();
        ParObject parObject = bundle.getParcelable("object");
        Log.d("SecondActivity", "the Name is " + parObject.getName() + ", age is " + 
             parObject.getAge());
    }

打印的日志如下:

10-26 15:00:58.694 24199 24199 D SecondActivity: the Name is Leon, age is 27

仔细一看,其实跟 Serializable 的用法大同小异,唯一的区别就是把 putSerializable 换成了 putParcelable,然后从第二个activity中去取对象的时候改成了 getParcelable。
可能会有人会问了,难道就这点区别吗?当然不止,既然Android提供一个更强大的传输对象的机制,想必它还能做更多的事,那就是 Parcelable 可以传输一个包含多个对象的列表:

public void startActivity(View view) {       
        Intent mIntent = new Intent(this, SecondActivity.class);
        Bundle bundle = new Bundle();
        ArrayList<ParObject> list = new ArrayList<>(2);
        list.add(new ParObject("Leon_1", 27));
        list.add(new ParObject("Leon_2", 28));
        bundle.putParcelableArrayList("object", list);
        mIntent.putExtras(bundle);
        startActivity(mIntent);
    }
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        Intent intent = getIntent();
        Bundle bundle = intent.getExtras();
        ArrayList<ParObject> list = bundle.getParcelableArrayList("object");
        if (list != null) {
            Log.d("SecondActivity", "size is " + list.size());
            for (ParObject parObject : list) {
                Log.d("SecondActivity", "Name is " + parObject.getName() + ", age is " + 
                     parObject.getAge());
            }
        }

    }

打印的日志如下:

10-26 15:12:28.023 26428 26428 D SecondActivity: size is 2
10-26 15:12:28.023 26428 26428 D SecondActivity: Name is Leon_1, age is 27
10-26 15:12:28.023 26428 26428 D SecondActivity: Name is Leon_2, age is 28

可以清楚的看到,从第一个activity传递了一个 ArrayList,第二个activity内成功获取了List。

我们再来确认下 bundle 有没有提供一个传输 Serializable 对象的List呢? 1.PNG 答案是:没有。 再来看看传输 Parcelable 对象还有哪些接口: 2.PNG

可以看到,除了能传输 List,还能传输数组,这就是 Parcelable 比 Serializable 强大的地方。

总结如下:
1. Parcelable 相比 serializable,实现它所花费的时间要长一些,也就是写的代码要多一些
2. Serializable 更容易实现(接口内没有任何需要复写的方法)
3. Serializable 使用了Java反射,在流的会话过程中创建了大量的临时对象,因此造成了相当大的GC
4. 在Android里面可以通过 Intent 传递 Parcelable 数组
5. 在Parcelable中我们可以编写自定义代码,所以它会创建较少的垃圾对象,也是因为这个自定义实现, 它比 serializable 更快,性能更好

相关文章

网友评论

      本文标题:Serializable与Parcelable的区别

      本文链接:https://www.haomeiwen.com/subject/lvltaktx.html