基本概念
序列化和反序列化
序列化和反序列化是指:将一个实例对象编码成字节流,并从字节流编码中重新构建对象实例的能力。
- 序列化
将一个对象编码成字节流。 - 反序列化
从一个字节流中读出一个对象实例。
反序列化是按照序列化写入的顺序读取的。
对象变量类型
- 序列化对象的时候只是针对变量进行序列化,不针对方法进行序列化
- 静态成员
static
变量属于类不属于对象,所以不参与序列化过程 - 用
transient
关键字标记的成员变量不参与序列化过程
两种方法
Java
提供的Serializable
Android
提供的Parcelable
Serializable
接口
Serializable
接口是 Java
提供的一个序列化接口,它是一个空接口,为对象提供标准的序列化和反序列化操作。使用 Serializable
来实现的对象的序列化相当简单,只需要在类中指定 serialVersionUID
标识即可自动实现默认的序列化过程。
serialVersionUID
- 概念
Java
的序列化机制是通过在运行时判断类的serialVersionUID
来验证版本一致性的。在进行反序列化时,JVM
会把传来的字节流中的serialVersionUID
与本地相应实体(类)的serialVersionUID
进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。 - 值
根据类名、接口名、成员方法及属性等来生成一个 64 位的哈希字段。默认值是1L
,但是Android
强烈建议显示指定一个值。 - 生成方法
Android Studio
自动生成serialVersionUID
的方法:
打开Settings
,切换到Editor->Inspections->Java->Serialization issues
,找到Serializationzable class without ‘serialVersionUID’
,将其勾选即可。或者直接搜索serialVersionUID
。
鼠标放在Serializable
接口的类名上,Android Studio
的智能提示(智能检查),就会多出了Add ‘serialVersionUID’ field
,完毕!
示例类
MyDataSerializable
自定义可以序列化的类,内部支持集合,但是静态变量和 tansient
申明的变量是无法序列化的。
1 | public class MyDataSerializable implements Serializable { |
测试代码
1 | private void testSerializable(){ |
结果分析
1 | 11:32: 6536/com.yD/:ShowIPCActivity:: testSerializable: myDataSerializable = |
从 Log
中可以看出:
transient
关键字
申明的变量不能序列化,反序列化的结果为0
。static
变量
定义为static
的变量,是属于类的,全局的,实际上并不参与序列化。所以读取到的static
变量值是全局数据区中的值,并不是反序列化产生的值,本例中表示改变后的值。
Parcelable
接口
Parcelable
接口是 Android SDK
提供的一种专门用于 Android
应用中对象的序列化和反序列化的方式,相比于 Seriablizable
具有更好的性能。实现 Parcelable
接口的对象就可以实现序列化并可以通过Intent
和 Binder
传递。
序列化的对象可以包含:基本数据类型,Bitmap
,List
,Map
,IBinder
等,以及所有已经实现 Parcelable
的类。
实现及重写
实现 Parcelable
的类, Android Studio
会给出需要实现和重写的方法模板:
1 | public static class Test implements Parcelable{ |
- 序列化
writeToParcel
表示序列化的过程,将变量都写入Parcel
。 - 反序列化
readFromParcel
表示反序列化的过程,从Parcel
中读取并生成类对象。 describeContents
该方法通常直接返回 0 。Parcelable
对象在扁平化中,如果包含文件描述符,则该值必须返回CONTENTS_FILE_DESCRIPTOR
。 参考 Parcelable API
这里“空构造方法”和 readFromParcel
两个都不是必须的。反序列化也可以在 createFromParcel
中直接反序列化;但是 AIDL
中反序列化时,必须定义“空构造方法”和 readFromParcel
才能成功反序列化。所以我们最好在自定义 Parcelable
中默认实现这两个方法养成好习惯。
反序列化是按照序列化的顺序读取的,所以
readFromParcel
反序列化读取变量的值时,一定要按照writeToParcel
写入的顺序读取。
示例类
1 | public class MyDataParcelable implements Parcelable { |
测试代码
1 | // Activity1 中初始化 MyDataParcelable, 启动 Activity2 |
结果分析
从结果上看,复杂数据对象 MyDataParcelable
成功的被序列化后传递出去,接收后能正常的反序列化解析出来。
1 | 16:02: 11509/com.yD/:ShowIPCActivity:: testParcelable: myDataParcelable = |
对比
Serializable
是Java
中的序列化接口,使用起来简单但是开销大,序列化和反序列化需要大量的I/O
操作。主要用于序列化到存储设备或者序列化后通过网络传输。Parcelable
是Android
提供的标准序列化接口,因此更适合Android
平台,缺点是使用稍微麻烦,但是效率高;主要用于内存序列化。