Android中Parcelable和Serializable的区别

  • 内容
  • 评论
  • 相关

进行Android开发的时候,我们都知道不能将对象的引用传给Activities或者Fragments,我们需要将这些对象放到一个Intent或者Bundle里面,然后再传递。
通过Android的API,我们知道有两种选择,即在传递对象时,需要对我们的对象进行 Parcelable 或者Serializable化。作为Java开发者,相信大家对Serializable 机制有一定了解,那为什么还需要 Parcelable呢?

为了回答这个问题,让我们分别来看看这两者的差异。

Serializable, 简单易用

public class XY implements Serializable {
    /** 姓名 */
    private String mName;
    /** 年龄 */
    private int mAge;
    /** 工作 */
    private List<String> mJobs;
    /** 朋友 */
    private List<Friend> mFriends;

    public String getName() {
        return mName;
    }

    public void setName(String name) {
        mName = name;
    }

    public int getAge() {
        return mAge;
    }

    public void setAge(int age) {
        mAge = age;
    }

    public List<String> getJobs() {
        return mJobs;
    }

    public void setJobs(List<String> jobs) {
        mJobs = jobs;
    }

    public List<Friend> getFriends() {
        return mFriends;
    }

    public void setFriends(List<Friend> friends) {
        mFriends = friends;
    }

    class Friend implements Serializable {
        /** 姓名 */
        private String mName;
        /** 年龄 */
        private int mAge;

        public String getName() {
            return mName;
        }

        public void setName(String name) {
            mName = name;
        }

        public int getAge() {
            return mAge;
        }

        public void setAge(int age) {
            mAge = age;
        }
    }
}

Serializable的迷人之处在于你只需要对某个类以及它的属性实现Serializable 接口即可。Serializable 接口是一种标识接口(marker interface),这意味着无需实现方法,Java便会对这个对象进行高效的序列化操作。

这种方法的缺点是使用了反射,序列化的过程较慢。这种机制会在序列化的时候创建许多的临时对象,容易触发垃圾回收。

Parcelable, 速度至上

public class XY implements Parcelable {
    /** 姓名 */
    private String mName;
    /** 年龄 */
    private int mAge;
    /** 工作 */
    private List<String> mJobs;
    /** 朋友 */
    private List<Friend> mFriends;

    public String getName() {
        return mName;
    }

    public void setName(String name) {
        mName = name;
    }

    public int getAge() {
        return mAge;
    }

    public void setAge(int age) {
        mAge = age;
    }

    public List<String> getJobs() {
        return mJobs;
    }

    public void setJobs(List<String> jobs) {
        mJobs = jobs;
    }

    public List<Friend> getFriends() {
        return mFriends;
    }

    public void setFriends(List<Friend> friends) {
        mFriends = friends;
    }

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.mName);
        dest.writeInt(this.mAge);
        dest.writeStringList(this.mJobs);
        dest.writeList(this.mFriends);
    }

    public XY() {
    }

    protected XY(Parcel in) {
        this.mName = in.readString();
        this.mAge = in.readInt();
        this.mJobs = in.createStringArrayList();
        this.mFriends = new ArrayList<Friend>();
        in.readList(this.mFriends, List.class.getClassLoader());
    }

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

        public XY[] newArray(int size) {
            return new XY[size];
        }
    };

    static class Friend implements Parcelable {
        /** 姓名 */
        private String mName;
        /** 年龄 */
        private int mAge;

        public String getName() {
            return mName;
        }

        public void setName(String name) {
            mName = name;
        }

        public int getAge() {
            return mAge;
        }

        public void setAge(int age) {
            mAge = age;
        }

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

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(this.mName);
            dest.writeInt(this.mAge);
        }

        public Friend() {
        }

        protected Friend(Parcel in) {
            this.mName = in.readString();
            this.mAge = in.readInt();
        }

        public static final Creator<Friend> CREATOR = new Creator<Friend>() {
            public Friend createFromParcel(Parcel source) {
                return new Friend(source);
            }

            public Friend[] newArray(int size) {
                return new Friend[size];
            }
        };
    }
    
}

根据 google 工程师的说法,这些代码将会运行地特别快。原因之一就是我们已经清楚地知道了序列化的过程,而不需要使用反射来推断。同时为了更快地进行序列化,对象的代码也需要高度优化。

因此,很明显实现Parcelable并不容易。实现Parcelable接口需要写大量的模板代码,这使得对象代码变得难以阅读和维护。

速度测试

当然,我们还是想知道到底Parcelable相对于Serializable要快多少。

测试方法

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

结果

123

parcelable-vs-serializable
NEXUS 10

Serializable: 1.0004ms,  Parcelable: 0.0850ms – 提升10.16倍。

NEXUS 4

Serializable: 1.8539ms – Parcelable: 0.1824ms – 提升11.80倍。

DESIRE Z

Serializable: 5.1224ms – Parcelable: 0.2938ms – 提升17.36倍。

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

总结

如果你想成为一个优秀的软件工程师,你需要多花点时间来实现 Parcelable ,因为这将会为你对象的序列化过程快10多倍,而且占用较少的资源。

但是大多数情况下, Serializable 的龟速不会太引人注目。你想偷点懒就用它吧,不过要记得serialization是一个比较耗资源的操作,尽量少使用。

如果你想要传递一个包含许多对象的列表,那么整个序列化的过程的时间开销可能会超过一秒,这会让屏幕转向的时候变得很卡顿。