序列化
序列化是将 java 对象转换成二进制字节序列,在 android 内存中传输数据时会经常用到序列化。比如说用 bundle 传输数据,bundle 在 android 组建通信中用的特别多,两种序列化操作都支持,Serializable 和 Parcelbale。
序列化有三种应用场景
- 数据持久化,将对象的字节流保存在本地
- 在网络传输中传递对象
- IPC 中传输对象
Serializable 接口
Serializable 接口是 java 自带的序列化接口,具体的序列化与反序列化操作是由 ObjectOutputStream 和 ObjectInputStream 完成的,然而 android 已经对这两个操作进行了封装。
序列化 ID
在使用 Serializable 接口的时候,还需要注意一点,就是要标注序列化 ID。下面说一下序列化 ID 的作用。
虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致(一般是 private static final long serialVersionUID = 1L)。
举个例子,客户端 A 将对象 C 序列化为二进制字节流传输给客户端 B,B 接收到字节流并反序列化。 如果这时候在客户端 A 和客户端 B 中类 C 的序列化 ID 不一样,那么接收端一方将无法反序列化。
现在再想想,为什么要这样设计呢,搞不一样的 ID 这不是给自己找麻烦吗,那就来想想不一样的 ID 有什么应用场景。
下面是摘自网上的一个用例
Client 端通过 Façade Object 才可以与业务逻辑对象进行交互。而客户端的 Façade Object 不能直接由 Client 生成,而是需要 Server 端生成,然后序列化后通过网络将二进制对象数据传给 Client,Client 负责反序列化得到 Façade 对象。该模式可以使得 Client 端程序的使用需要服务器端的许可,同时 Client 端和服务器端的 Façade Object 类需要保持一致。当服务器端想要进行版本更新时,只要将服务器端的 Façade Object 类的序列化 ID 再次生成,当 Client 端反序列化 Façade Object 就会失败,也就是强制 Client 端从服务器端获取最新程序。
还有几点要注意的
- 静态变量不能序列化
- Transient 标注的变量不会被序列化
- 父类不会被序列化
- 序列化存储时不会存储相同的对象,会存储以存储对戏那个的索引
Parcelable 接口
Parcelable 接口是用来解决 Serializable 接口效率问题的(Serializable 会产生大量的临时变量,引起频繁的 GC)。Parcelable 的使用场景也是因此而定的,序列化对象在网络中或者组建间,或者进程间传输的时候适合用 Parcelable 接口。
Parelable 接口也有一个缺点,那就是不能使用在要将数据存储在磁盘上的情况(如:永久性保存对象,保存对象的字节序列到本地文件中),因为 Parcel 本质上为了更好的实现对象在 IPC 间传递,并不是一个通用的序列化机制,当改变任何 Parcel 中数据的底层实现都可能导致之前的数据不可读取,所以此时还是建议使用 Serializable 。
使用场景
引发我对序列化问题学习的原因是当我使用 fragment 的时候,使用 newInstance 方法实例化 fragment(使用 newInstance()方法的好处可以自行百度),需要对参数用 bundle 进行保存。


可以看到 bundle 支持的是序列化对象,String 类型也是继承 Serializable 接口的。

在 newInstance()方法里面进行保存以后,在 onCreate 方法中读取保存的数据。
1 | @Override |
这里使用的是 Serializable 接口,得说明一下,尽量使用 Parcelable 接口,速度比 Serializable 接口快了 10 倍左右。


