android 序列化
android 序列化
什么是序列化
将 java 对象转化为二进制过程,就是序列化,
将二进制转化为 java 对象的过程,就是反序列化
为什么要序列化
在下面几种场景下我们需要序列化
- 永久性保存对象,保存对象的字节序列到本地文件中;
- 对象在网络中传递;
- 对象在 IPC 间传递(进程通信)
如何序列化
序列化有两种方法,java 中自带的实现 Serializable,android 中特有的 Parcelable
Serializable
Serializable 接口是一个标记接口,不用实现任何方法。一旦实现了此接口,该类的对象就是可序列化的。
新建类实现 Serializable,Serializable 为空接口,没有要实现的方法, 需要 定义一个静态常量 serialVersionUID
1 |
|
这样这个类的序列化就完成了。
Serializable 序列化过程其实是将数据写入到文件,而反序列化则是将读取文件中的数据,重新生成对象。
Serializable 原理
- 序列化:将对象写入到 IO 流中,并保存信息到文件中
- 反序列化:从文件中读取信息, 然后构建 IO 流中恢复对象。 反序列化过程并没有调用构造函数,而是 JVM 生成
Parcelable
1 | public class ParBean implements Parcelable { |
Parcelable 序列号是在内存空间进行的。
Parcelable 原理
Parcelable 是序列话过程是通过 native 函数进行的,属性是通过指针偏移来保存信息,所以反序列化过程读取属性的顺序必须要与写入属性的过程一样,否则会反序列化失败。
简单理解就是将 java 中对象的各个属性都保存到 native 空间,然后将各个地址指针存放到 java 引用中,
当反序列化时,调用 writeToParcel 写入保存到 native 空间中的属性,然后调用 createFromParcel 生成实例对象,然后就完成了整个反序列化过程。
writeToParcel 中的 write 顺序要与 protected 构造函数中的 read 顺序保持一致,否则会在反序列化时出错。
序列化方案区别
上面讲了两个序列化方案,
Serializable:是 java 就有的,代码量少,但在序列化时,会产生大量的临时对象,容易造成频繁的 minor GC
Parcelable:android 特有的,代码量比 Serializable 要多,但使用效率高,且没那么占内存
因为在选择序列化时,优先使用 Parcelable,但有一种情况特殊,当数据保存在磁盘上时,Parcelable 在外界有变化的情况下,
不能很好的保证数据的连续性,因此在此种场景下推荐使用 Serializable;
选择序列化方法的原则
1)在使用内存的时候,Parcelable 比 Serializable 性能高,所以推荐使用 Parcelable。
2)Serializable 在序列化的时候会产生大量的临时变量,从而引起频繁的 GC。
3)Parcelable 不能使用在要将数据存储在磁盘上的情况,因为 Parcelable 不能很好的保证数据的持续性在外界有变化的情况下。尽管 Serializable 效率低点,但此时还是建议使用 Serializable 。
序列化某种程度来说并不安全
因为序列化的对象数据转换为二进制,并且完全可逆。但是在 RMI 调用时
所有 private 字段的数据都以明文二进制的形式出现在网络的套接字上,这显然是不安全的解决方案
1、 序列化 Hook 化(移位和复位)
2、 序列数据加密和签名
3、 利用 transient 的特性解决
4、 打包和解包代理
补充
- static 和 transient 字段不能被序列化(感兴趣的同学可以深入研究下)
因为 static 修饰的变量不属于对象,而是属于类
PS:
如果一个可序列化的类的成员不是基本类型,也不是 String 类型,那这个引用类型也必须是可序列化的;否则,会导致此类不能序列化。