0%

Serializable 和 Parcelable

什么是序列化?

序列化本质上就是把对象内存中的数据按照一定规则,变成一系列的字节数据(二进制数据),然后再把这些字节数据写入到流中。而反序列化的过程则相反,先读取字节数据,然后再重新组装成Java对象。

序列化的目的

  • 永久的保存对象数据(将对象数据保存在文件当中,或者磁盘中)
  • 通过序列化操作将对象数据在网络上进行传输(由于网络传输是以字节流的方式对数据进行传输的,因此序列化的目的是将对象数据转换成字节流的形式)
  • 将对象数据在进程之间传递
  • 序列化对象的时候只是针对变量进行序列化,不针对方法进行序列化
  • 在Intent之间,基本数据类型直接进行相关传递即可,但是一旦数据类型比较复杂的时候,就需要进行序列化操作了

Android中实现序列化的两种方式

1.implements Serializable接口
2.implements Parcelable接口(还需要实现接口内部的相应方法,写入数据的顺序和读出数据的顺序必须相同)

Serializable原理

Java中的对象流操作是通过ObjectInputStream和ObjectOutputStream这两个流对象来实现的,在两个对象的使用过程中,源码里会判断对象是否实现Serializable接口,否则会抛出java.io.NotSerializableException。Serializable也可以自定义序列化过程,实现writeObject和readObject方法即可。

1.Serializable只是一个接口,本身没有任何实现;
2.对象的反序列化并没有调用对象的任何构造方法;
3.serialVersionUID是用于记录文件版本信息的,最好能够自定义。否则系统会自动生成一个serialVersionUID,文件或者对象的任何改变,都会改变serialVersionUID,导致反序列化的失败,如果自定义就没有这个问题;
4.如果某个属性不想实现序列化,可以采用transient修饰;
5.Serializable的系统实现是采用ObjectOutputStream和ObjectInputStream实现的,这也是为什么调用ObjectInputStream和ObjectOutputStream时,需要对应的类实现Serializable接口。

使用Parcelable进行序列化操作

1.writeToParcel将对象数据序列化成一个Parcel对象;
2.重写describeContents方法,默认值为0;
3.public static final Creator CREATOR同时需要实现两个方法:
createFromParcel(Parcel in)从Parcel容器中取出数据并进行转换
newArray(int size) 返回对象数据的大小

Parcelable原理

无论是对数据的读还是写都需要使用Parcel作为中间层将数据进行传递。通过JNI相互调用,在java层先创建Parcel对象,然后在调用相关的读写操作时,首先将Java的Parcel对象转换成Parcel c++对象,然后被封装在Parcel中的相关数据由c++底层完成数据的序列化操作。
源码参考:
https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/jni/android_os_Parcel.cpp

https://cs.android.com/android/platform/superproject/+/refs/heads/master:frameworks/native/libs/binder/Parcel.cpp

  • 整个读写全是在内存中进行,主要是通过malloc()、realloc()、memcpy()等内存操作进行,所以效率比Java系列化中使用外部存储器会高很多
  • 对于普通数据,使用的是mData内存地址,对于IBinder类型的数据以及FileDescriptor使用的是mObjects内存地址。后者是通过flatten_binder()和unflatten_binder()实现的,目的是反序列化时读出的对象就是原对象而不是重新new一个新对象。

使用时如何选择

  • 内存方面,Parcelable比Serializable性能高,所以推荐使用Parcelable。
  • Serializable在序列化的时候会产生大量的临时变量,从而引起频繁GC。
  • Parcelable不能使用在要将数据存储在磁盘的情况,此时建议使用Serializable。

对比

Serializable Parcelable
存储媒介 IO读写存储在硬盘上 直接在内存中读写
性能
使用 实现接口即可 需要编写代码
效率 大量使用反射,效率低 内存操作,效率高