2.2 IPC 基础概念 - Serializable 接口、Parcelable 接口

前言:Serializable 接口 和 Parcelable 接口可以完成对象的序列化过程,当我们需要通过 Intent 和 Binder 传输数据时就需要使用 Parcelable 或者 Serializable。还有的时候我们需要把对象持久化到存储设备上或者通过网络传输到其他客户端,这个时候也需要使用 Serializable 来完成对象的持久化。

Serializable 接口

Serializable 是 Java 所提供的一个序列化接口,它是一个空接口,为对象提供标准的序列化和反序列化操作。只需要类实现 Serializable 接口并声明一个 serialVersionUID 即可,实际上,甚至这个 serialVersionUID 也不是必需的。

public class User implements Serializable{
        private static final long serialVersionUID = 519067123721295773L;

        private int userId;
        private String userName;
    }

只需要采用 ObjectOutputStream 和 ObjectInputStream 即可轻松实现对象的序列化和反序列化。

其中序列化过程中,有一个 serialVersionUID,它的作用是用来辅助序列化和反序列化过程的,原则上序列化后的数据中的 serialVersionUID 只有和当前类的 serialVersionUID 相同才能够正常地被反序列化。

serialVersionUID 的工作机制是这样的:序列化的时候系统会把当前类的 serialVersionUID 写入序列化文件中(也可能是其他中介),当反序列化的时候系统会去检测文件中的 serialVersionUID,看它是否和当前类的 serialVersionUID 一致,如果一致就说明序列化的类的版本和当前类的版本是相同的,这个时候可以成功反序列化;否则就说明当前类和序列化的类相比发生了某些变换,比如成员变量的数量、类型可能发生了改变,这个时候是无法正常反序列化的。

Parcelable 接口

parcelable 是 Android 提供的序列化方式。只要实现这个接口,一个类的对象就可以实现序列化并可以通过 Intent 和 Binder 传递。

public class User implements Parcelable {
        private int userId;
        private String userName;
        private boolean isMale;

        public User(int userId, String userName, boolean isMale) {
            this.userId = userId;
            this.userName = userName;
            this.isMale = isMale;
        }

        private User(Parcel in) {
            userId = in.readInt();
            userName = in.readString();
            isMale = in.readByte() != 0;
        }

        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];
            }
        };

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

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

Parcel 内部包装了可序列化的数据,可以在 Binder 中自由传输。序列化功能由 writeToParcel 方法来完成,最终是通过 Parcel 中的一系列 write 方法来完成的;反序列化功能由 CREATOR 来完成,其内部标明了如何创建序列化对象和数组,并通过 Parcel 的一系列 read 方法来完成反序列化过程;内容描述功能由 describeContents 方法来完成,几乎在所有情况下这个方法都应该返回 0,仅当当前对象中存在文件描述符时,此方法返回 1。

两者的选择

Serializable 是 Java 中的序列化接口,其使用起来简单但是开销很大,序列化和反序列化需要大量 I/O 操作。

Parcelable 是 Android 中的序列化方式,因此更适合用在 Android 平台上,它的缺点就是使用起来稍微麻烦点,但是效率很高。

Parcelable 主要用在内存序列化上,通过 Parcelable 将对象序列化到存储设备中或者将对象序列化后通过网络传输也都可以,但是过程会稍显复杂,建议使用 Serializable。