引言:
Android中Serialzable和Parcelable是我们常用的两个接口,比如通过intent传输一个对象的时候。
本文主要解答以下问题:
1、序列化是什么?
2、一个对象用Serialzable序列化后是什么样的?
3、一个对象用Parcelable序列化后是什么样的?
4、Parcelable和Serialzable哪个更快?用哪个?
序列化是什么
一个小问题:为什么intent不能直接放入Object?
进程间内存不共享,所以要用序列化后的数据进行进程间的通信。
序列化:
将对象的状态信息转换为可以存储或传输的形式的过程
比如:Java对象转为Json
为什么需要序列化?
1、跨进程通信、网络通信。
进程内通信:传一个句柄、内存地址等,访问同一对象
2、数据的持久化:
需要把数据转换成字节序列存储在文件中。
Android中常用的序列化方式
实现Serialzable
实现Parcelable
Json
protobuf......
Serializable序列化过程
看原理之前,我们先看一个对象经过Serializable序列化后是什么样的
一个Serializable序列化的Demo:
package com.ywj.serialtest;
import java.io.Serializable;
public class Phone implements Serializable {
private static final long serialVersionUID = 1L;
private String brand;
public void setBrand(String brand) {
this.brand = brand;
}
}
public void main() {
String FILE_PATH = path + "info.txt";
try {
File f = new File(FILE_PATH);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(FILE_PATH));
Phone phone = new Phone();
phone.setBrand("OnePlus");
out.writeObject(phone);
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//输出结果:
aced 0005 7372 0018 636f 6d2e 7977 6a2e
7365 7269 616c 7465 7374 2e50 686f 6e65
0000 0000 0000 0001 0200 014c 0005 6272
616e 6474 0012 4c6a 6176 612f 6c61 6e67
2f53 7472 696e 673b 7870 7400 074f 6e65
506c 7573
aced:STREAM_MAGIC,声明使用了序列化协议
0005:STREAM_VERSION,序列化协议版本
73:TC_OBJECT,声明这是一个新的对象
72:TC_CLASSDESC,声明这里开始一个新Class
0018:Class包名长度 com.ywj.serialtest 共18位 (类全称 com.ywj.serialtest.Phone)
636f 6d2e 7977 6a2e
7365 7269 616c 7465 7374 2e50 686f 6e65
包名+类名,对应ASCII 十六进制 63(c)6f(o) 6d(m)2e(.) 79(y)77(w) 6a(j)2e(.)
73(s) 65(e)72(r) 69(i) 61(a) 6c(l) 7465 7374 (test) 2e50 686f 6e65(.phone)
0000 0000 0000 0001 SerialVersionUID
02:标记号,该值声明该对象支持序列化
00 01 该类所包含的变量个数
4c:L 类型object
obj_typecode:
[ // array
`L' // object
00 05:变量名字的长度
6272 616e 64 brand
74 TC_STRING 代表String类型
0012 长度
4c:L
6a61 7661 2f6c 616e 672f 5374 7269 6e67 3b4c java.lang.String;
78 TC_ENDBLOCKDATA 对象块结束标识
70 TC_NULL 表示没有超类了(父类)
74 TC_STRING 代表String类型
00 07 长度
4f 6e65 506c 7573 Oneplus
可参考以下官方文档及java.io.ObjectStreamConstants中常量
6.4.1 Rules of the Grammar
A Serialized stream is represented by any stream satisfying the stream rule.
stream:
magic version contents
contents:
content
contents content
content:
object
blockdata
. . . . . .
Serializable序列化过程
序列化入口类:ObjectOutputStream
输出到文件的核心方法如下:
-> java.io.ObjectOutputStream#writeObject0
-> java.io.ObjectOutputStream#writeOrdinaryObject
-> java.io.ObjectOutputStream#writeSerialData
-> java.io.ObjectOutputStream#defaultWriteFields
-> java.io.ObjectOutputStream#writeObject0
private void writeObject0(Object obj, boolean unshared)throws IOException
{
..................
Class<?> cl = obj.getClass();
..................
ObjectStreamClass desc;
desc = ObjectStreamClass.lookup(cl, true);
..................
} else if (obj instanceof Serializable) {
writeOrdinaryObject(obj, desc, unshared);
} else {
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
..................
先看ObjectStreamClass.lookup核心代码
..................
// Object entry是从WeakReference里取的缓存,如果没有缓存则new ObjectStreamClass
if (entry == null) {
try {
entry = new ObjectStreamClass(cl);
} catch (Throwable th) {
entry = th;
}
}
..................
-> java.io.ObjectStreamClass#ObjectStreamClass(java.lang.Class<?>)
private ObjectStreamClass(final Class<?> cl) {
// 代表的类的类型this.cl = cl;// 类名
name = cl.getName();
// 是否是动态代理产生的类
isProxy = Proxy.isProxyClass(cl);
// 是否是Enum
isEnum = Enum.class.isAssignableFrom(cl);
// 是否实现了 serializable
serializable = Serializable.class.isAssignableFrom(cl);
// 是否实现了 externalizable
externalizable = Externalizable.class.isAssignableFrom(cl);
// 父类类型
Class<?> superCl = cl.getSuperclass();
// superDesc是一个ObjectStreamClass,代表父类的描述
superDesc = (superCl != null) ? lookup(superCl, false) : null;
// 指向自己的ObjectStreamClass
localDesc = this;
if (serializable) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
if (isEnum) {
// serialVersionUID
suid = Long.valueOf(0);
// Enum没有属性
fields = NO_FIELDS;
return null;
}
if (cl.isArray()) {
// 为集合的话,没有属性
fields = NO_FIELDS;
return null;
}
// 反射获取serialVersionUID
suid = getDeclaredSUID(cl);
try {
// 获取此类所有需要被序列化的属性,即Filed
fields = getSerialFields(cl);
computeFieldOffsets();
} catch (InvalidClassException e) {
serializeEx = deserializeEx =
new ExceptionInfo(e.classname, e.getMessage());
fields = NO_FIELDS;
}
if (externalizable) {
// 实现了Enternalizable则反射获取构造方法
cons = getExternalizableConstructor(cl);
} else {
// 反射获取构造方法
cons = getSerializableConstructor(cl);
// 反射获取writeObject方法,实现了则有
writeObjectMethod = getPrivateMethod(cl, "writeObject",
new Class<?>[] { ObjectOutputStream.class },
Void.TYPE);
// 反射获取readObject方法,实现了则有
readObjectMethod = getPrivateMethod(cl, "readObject",
new Class<?>[] { ObjectInputStream.class },
Void.TYPE);
// 反射获取readObjectNoData方法,实现了则有
readObjectNoDataMethod = getPrivateMethod(
cl, "readObjectNoData", null, Void.TYPE);
hasWriteObjectData = (writeObjectMethod != null);
}
// 反射获取是否实现了 writeReplace,实现了则有
writeReplaceMethod = getInheritableMethod(
cl, "writeReplace", null, Object.class);
// 反射获取是否实现了 readResolve,实现了则有
readResolveMethod = getInheritableMethod(
cl, "readResolve", null, Object.class);
return null;
}
});
} else {
suid = Long.valueOf(0);// serialVersionUID为0
fields = NO_FIELDS;
}
......
}
java.io.ObjectOutputStream#writeObject0
java.io.ObjectOutputStream#writeOrdinaryObject
java.io.ObjectOutputStream#writeSerialData
private void writeOrdinaryObject(Object obj,
ObjectStreamClass desc,
boolean unshared) throws IOException {
try {
//检查ObjectStreamClass对象
desc.checkSerialize();
//写入字节0x73,即代表一个新对象(此处也可能写入数组)
bout.writeByte(TC_OBJECT);
//写入class信息,**此处会从HandleTable handles;中查找是否有缓存,有则写入TC_REFERENCE标识及缓存索引
writeClassDesc(desc, false);
// 如果unshared为true的话,每次都会重新解析出对象描述,// 正常情况为false,因此对象描述可以被handles缓存复用
handles.assign(unshared ? null : obj);
if (desc.isExternalizable() && !desc.isProxy()) {
//是否实现了Externalizable与不是代理类型
// 主要调用 void writeExternal(ObjectOutput out)
// void readExternal(ObjectInput in)
writeExternalData((Externalizable) obj);
} else {
//写入该对象变量信息及其父类的成员变量
writeSerialData(obj, desc);
}
} finally {
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
}
-> java.io.ObjectOutputStream#writeSerialData
private void writeSerialData(Object obj, ObjectStreamClass desc)throws IOException {
//获取当前类以及其父类所有实现Serializable的ObjectStreamClass
ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
for (int i = 0; i < slots.length; i++) {
ObjectStreamClass slotDesc = slots[i].desc;
//判断writeObject method 是否为null
if (slotDesc.hasWriteObjectMethod()) {
PutFieldImpl oldPut = curPut;
curPut = null;
SerialCallbackContext oldContext = curContext;
try {
curContext = new SerialCallbackContext(obj, slotDesc);
bout.setBlockDataMode(true);
//反射调用重写过的writeObject()方法
slotDesc.invokeWriteObject(obj, this);
bout.setBlockDataMode(false);
bout.writeByte(TC_ENDBLOCKDATA);
} finally {
curContext.setUsed();
curContext = oldContext;
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
curPut = oldPut;
} else {
//否则走默认写入规则
defaultWriteFields(obj, slotDesc);
}
}
}
-> java.io.ObjectOutputStream#defaultWriteFields
private void defaultWriteFields(Object obj, ObjectStreamClass desc)throws IOException {
Class<?> cl = desc.forClass();
if (cl != null && obj != null && !cl.isInstance(obj)) {
//obj不是cl类型实例throw new ClassCastException();
}
//检查中间过程是否有非法信息
desc.checkDefaultSerialize();
int primDataSize = desc.getPrimDataSize();
if (primVals == null || primVals.length < primDataSize) {
primVals = new byte[primDataSize];
}
desc.getPrimFieldValues(obj, primVals);
bout.write(primVals, 0, primDataSize, false);
//获取当前类的所有字段信息
ObjectStreamField[] fields = desc.getFields(false);
Object[] objVals = new Object[desc.getNumObjFields()];
int numPrimFields = fields.length - objVals.length;
//返回获取每个Field的值,保存在objVals中//这里就是通过反射获取到Field在当前obj中对应的值
desc.getObjFieldValues(obj, objVals);
for (int i = 0; i < objVals.length; i++) {
try {
//根据实际类型调用
//这里会递归调用writeObject0方法,
//writeObject0方法的最后根据实际类型写入
//如果是引用类型,又会重复该过程
writeObject0(objVals[i],
fields[numPrimFields + i].isUnshared());
} finally {
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
}
}
-> java.io.ObjectOutputStream#writeObject0
..................
if (obj instanceof Class) {
writeClass((Class) obj, unshared);
} else if (obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
// END Android-changed
} else if (obj instanceof String) {
writeString((String) obj, unshared);
} else if (cl.isArray()) {
writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
writeEnum((Enum<?>) obj, desc, unshared);
} else if (obj instanceof Serializable) {
// 成员变量又递归到了最初的writeOrdinaryObject方法
writeOrdinaryObject(obj, desc, unshared);
} else {
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
..................
->writeString((String) obj, unshared) 为例
private void writeString(String str, boolean unshared) throws IOException {
handles.assign(unshared ? null : str);
long utflen = bout.getUTFLength(str);
if (utflen <= 0xFFFF) {
bout.writeByte(TC_STRING);//代表是String类型
bout.writeUTF(str, utflen);
} else {
bout.writeByte(TC_LONGSTRING);//代表是长String类型
bout.writeLongUTF(str, utflen);
}
}
再往下就是文件读写操作,不深入看了。
反序列化(读取)过程
-> java.io.ObjectInputStream#readObject
-> java.io.ObjectInputStream#readObject0
-> java.io.ObjectInputStream#readOrdinaryObject
-> java.io.ObjectInputStream#readSerialData
-> java.io.ObjectInputStream#defaultReadFields
-> java.io.ObjectInputStream#readObject0
过程和序列化基本相似,从readObject0递归读取所有成员,最后反射生成实例对象。
小结:
Serializable 将对象当做一棵树,遍历反射各个节点获取信息,会产生大量中间变量来保存信息,但对于同一个class可以节省一定空间。
Serializable 序列化支持替代默认流程,它会先反射判断是否存在我们自己实现的序列化方法 。可以自定义序列化的方法如下:
对象侧:
- writeObject(): 序列化前操作对象数据,比如加密
- readObject(): 反序列化前操作对象数据,比如解密
- readObjectNoData():对象没有属性数据时,可以提供特殊处理
- writeReplace():替换序列化的对象
- readResolve(): 替换反序列化后的对象
解析侧:
-
writeObjectOverride(): 自定义序列化过程
-
replaceObject():切面替换要序列化的对象。
Parcelable
Parcelable是google推出的,专门用于进程间通信的高效序列化方式。
android.os.Bundle#putParcelable
数据写入
public void putParcelable(@Nullable String key, @Nullable Parcelable value) {
// 主要是创建一个大小为value的内存数据块大小的ArrayMap,赋值给mMap。// 如果mMap已经有了就扩容,后面传输会把mMap里的数据统一序列化。
unparcel();
// 放数据,mMap是一个 ArrayMap
mMap.put(key, value);
// 调试用标志位,不用关注
mFlags &= ~FLAG_HAS_FDS_KNOWN;
}
Activity数据传输过程会通过AMS进行数据通信,最终调用到
android.content.Intent#writeToParcel
public void writeToParcel(Parcel out, int flags) {
// 写入Action、flag等信息out.writeString8(mAction);
Uri.writeToParcel(out, mData);
out.writeString8(mType);
....................................
// 写入我们自定义bundle,主要看这块的格式
out.writeBundle(mExtras);
}
-> Parcel.writeBundle()
-> Bundle.writeToParcel()
-> BaseBundle.writeToParcelInner()
void writeToParcelInner(Parcel parcel, int flags) {
// If the parcel has a read-write helper, we can't just copy the blob, so unparcel it first.
if (parcel.hasReadWriteHelper()) {
// 有ReadWriteHelper,默认为ReadWriteHelper.DEFAULT,需要先将数据压入mMap
unparcel();
}
final ArrayMap<String, Object> map;
synchronized (this) {
// unparcel() can race with this method and cause the parcel to recycle// at the wrong time. So synchronize access the mParcelledData's content.// unparcel()会把mParcelledData数据取出,这里主要解决Bundle读写同步问题,主要是直接用 Bundle(Parcel parcel) 创建 Bundle 的情境。if (mParcelledData != null) {if (mParcelledData == NoImagePreloadHolder.EMPTY_PARCEL) {// 无数据,写入Int的0作为标志
parcel.writeInt(0);
} else {
int length = mParcelledData.dataSize();
parcel.writeInt(length);
parcel.writeInt(mParcelledByNative ? BUNDLE_MAGIC_NATIVE : BUNDLE_MAGIC);
// 最终通过Parcel.cpp.appendFrom() 将mParcelledData的数据内容拷贝到parcel中
parcel.appendFrom(mParcelledData, 0, length);
}
return;
}
map = mMap;
}
// Special case for empty bundles.
if (map == null || map.size() <= 0) {
parcel.writeInt(0);
return;
}
// 实际上是 Parcel.cpp.mDataPos
int lengthPos = parcel.dataPosition();
// 写入临时占用位,用于写入数据长度
parcel.writeInt(-1); // dummy, will hold length
// 写入魔数 private static final int BUNDLE_MAGIC = 0x4C444E42; // 'B' 'N' 'D' 'L'
parcel.writeInt(BUNDLE_MAGIC);
// 也是Parcel.cpp.mDataPos,但这个时候多了 占用位 + 魔数 数据长度的偏移量
int startPos = parcel.dataPosition();
// 把 map 的数据写入 parcel
parcel.writeArrayMapInternal(map);
// 最终Parcel.cpp.mDataPos的位置
int endPos = parcel.dataPosition();
// 回到lengthPos位置
parcel.setDataPosition(lengthPos);
int length = endPos - startPos;
// 在前面占用的位置上,写入长度信息
parcel.writeInt(length);
// 恢复Parcel.cpp.mDataPos到正确的位置
parcel.setDataPosition(endPos);
}
-> parcel.writeArrayMapInternal(map);
/**
* Flatten an ArrayMap into the parcel at the current dataPosition(),
* growing dataCapacity() if needed. The Map keys must be String objects.
*//* package */ void writeArrayMapInternal(@Nullable ArrayMap<String, Object> val) {
if (val == null) {
writeInt(-1);
return;
}
//附件参数的长度
final int N = val.size();
//写入时Map的长度
writeInt(N);
int startPos;
for (int i = 0; i < N; i++) {
//写入key
writeString(val.keyAt(i));
//写入value
writeValue(val.valueAt(i));
}
}
-> writeValue(val.valueAt(i));
// 写入Parcelable的各种值。如果是map、bundle等类型会递归调用writeToParcelInner
public final void writeValue(@Nullable Object v) {
if (v == null) {
writeInt(VAL_NULL);
} else if (v instanceof String) {
writeInt(VAL_STRING);
writeString((String) v);
} else if (v instanceof Integer) {
writeInt(VAL_INTEGER);
writeInt((Integer) v);
............
} else if (v instanceof Parcelable) {
// IMPOTANT: cases for classes that implement Parcelable must// come before the Parcelable case, so that their specific VAL_*// types will be written.
writeInt(VAL_PARCELABLE);
writeParcelable((Parcelable) v, 0);
............
-> writeParcelable((Parcelable) v, 0)
public final void writeParcelable(@Nullable Parcelable p, int parcelableFlags) {
if (p == null) {
writeString(null);
return;
}
// 写入实现Parcelable的className,格式:String。
// ***Parcelable 没有对 class 名称复用,与Serializable不同***
writeParcelableCreator(p);
// 此处调用我们实现parcelable接口类的writeToParcel方法
p.writeToParcel(this, parcelableFlags);
}
先看一下writeParcelableCreator()
public final void writeParcelableCreator(@NonNull Parcelable p) {
String name = p.getClass().getName();
// 直接写入类名,没有像 Serializable 一样用了索引
writeString(name);
}
接着看p.writeToParcel(),调用了我们实现 Parcelable 类重写的方法
@Overridepublic void writeToParcel(Parcel dest, int flags) {
dest.writeString(brand);
dest.writeInt(number);
}
Parcelable序列化后的格式大致如下:
左右部分为Activity启动过程写入的其他信息,length为Bundle数据长度,magic为标示,N为数据量,然后,是连续的K/V,K后面包含一个Int,用来标志对象类型。如果是 Parcelable 类型,key 后面还会跟一个 String格式的类名。Parcelable 对象value的写入,交给对象的实现类实现writeToParcel()来写入。
写入过程:先跳过 lenght,写入MAGIC_NUMBER,object.size,然后调用我们自定义的writeToParcel(),写完后回到 length 写入实际长度,然后移动 Parcel 指针跳到末尾。
反序列化(读取)过程
public <T extends Parcelable> T getParcelable(@Nullable String key
{
unparcel();
Object o = mMap.get(key);
if (o == null) {
return null;
}
try {
return (T) o;
} catch (ClassCastException e) {
typeWarning(key, o, "Parcelable", e);
return null;
}
}
-> BaseBundle.unparcel()
-> BaseBundle.initializeFromParcelLocked()
-> Parcel.readArrayMapInternal()
void readArrayMapInternal(@NonNull ArrayMap outVal, int N,
@Nullable ClassLoader loader) {
// N 是在前一步initializeFromParcelLocked()通过Parcel.readInt()读出
......
// 数据起始位置
int startPos;
while (N > 0) {
// key
String key = readString();
// 读出value,value包含int(标志对象类型)和数据信息
Object value = readValue(loader);
......
// 存入mMap中
outVal.append(key, value);
N--;
}
outVal.validate();
}
-> Parcel.readValue()
-> Parcel.readParcelable()
public final <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader) {
// 这个方法主要是获取Parcelable的CREATOR,先调用readString()拿到类名,从静态变量mCreators去取,取不到就反射去获取对应类的CREATOR
Parcelable.Creator<?> creator = readParcelableCreator(loader);
if (creator == null) {
return null;
}
// 调用Creator.createFromParcel,创建实例对象if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
Parcelable.ClassLoaderCreator<?> classLoaderCreator =
(Parcelable.ClassLoaderCreator<?>) creator;
return (T) classLoaderCreator.createFromParcel(this, loader);
}
return (T) creator.createFromParcel(this);
}
// Cache of previously looked up CREATOR.createFromParcel() methods for particular classes. Keys are the names of the classes, values are Method objects.
private static final HashMap<ClassLoader,HashMap<String,Parcelable.Creator<?>>>
mCreators = new HashMap<>();
public static final Creator<ParcelPhone> CREATOR = new Creator<ParcelPhone>() {
@Override
public ParcelPhone createFromParcel(Parcel in) {
return new ParcelPhone(in);
}
@Override
public ParcelPhone[] newArray(int size) {
return new ParcelPhone[size];
}
};
protected ParcelPhone(Parcel in) {
brand = in.readString();
number = in.readInt();
}
Parcel序列化的原理总结为:
-
实际存储由Parcel.cpp负责,在一块连续的内存区域的中,写入各类信息。Parcel在实例化时,拿到Parcel.cpp的实例的句柄
-
Parcel在序列化写入数据时,遵循Int-Value的形式,其中Int指数据类型,Value指实际数据
-
要序列化的对象实现Parcelable,其中writeToParcel()在序列化过程中获得执行时机,对象负责写入数据。CREATOR在反序列化时被调用,调用对象构造函数,将Parcel传入以让对象填充数据
大家都知道,Parcelable是不能用于存储数据的,但为什么?
其实Parcel提供了将数据输出为字节数组的方式,理论上是可以存储的,见Parcel.cpp代码:
android.os.Parcel#marshall
public final byte[] marshall() {
return nativeMarshall(mNativePtr);
}
但google官网有一段这样的说明:
一句话:不同Android版本Parcel的api可能不同,导致系统升级后旧数据无法读取
Serialzable和Parcelable效率对比
新建一个ParcelPhone类,里面两个成员变量
public class ParcelPhone implements Parcelable, Serializable {
private String brand;
private int number;
public ParcelPhone() {
}
protected ParcelPhone(Parcel in) {
brand = in.readString();
number = in.readInt();
}
@Overridepublic void writeToParcel(Parcel dest, int flags) {
dest.writeString(brand);
dest.writeInt(number);
}
@Overridepublic int describeContents() {
return 0;
}
public static final Creator<ParcelPhone> CREATOR = new Creator<ParcelPhone>() {
@Overridepublic ParcelPhone createFromParcel(Parcel in) {
return new ParcelPhone(in);
}
@Overridepublic ParcelPhone[] newArray(int size) {
return new ParcelPhone[size];
}
};
public void setNumber(int number) {
this.number = number;
}
public void setBrand(String brand) {
this.brand = brand;
}
测试代码:
private void doParcalable() {
long writeStart;
long writeEnd;
long readStart;
long readEnd;
Parcel parcel;
int dataStartPos;
ParcelPhone curPhone;
parcel = Parcel.obtain();
curPhone = createPhone();
writeStart = System.nanoTime();
dataStartPos = parcel.dataPosition();
parcel.writeParcelable(curPhone, 0);
writeEnd = System.nanoTime();
int length = parcel.marshall().length;
parcel.setDataPosition(dataStartPos);
readStart = System.nanoTime();
ParcelPhone.CREATOR.createFromParcel(parcel);
readEnd = System.nanoTime();
Log.d("PARCELTEST", "parcel: " +
(writeEnd - writeStart) / 1000 + "微秒; unparcel: " +
(readEnd - readStart) / 1000 +
"微秒; Size: " + length);
}
private void doSerializable() {
long writeStart;
long writeEnd;
long readStart;
long readEnd;
ByteArrayOutputStream dataOut;
ByteArrayInputStream dataIn;
try {
ObjectOutputStream out;
ObjectInputStream in;
dataOut = new ByteArrayOutputStream();
out = new ObjectOutputStream(dataOut);
ParcelPhone phone = createPhone();
writeStart = System.nanoTime();
out.writeObject(phone);
writeEnd = System.nanoTime();
out.flush();
byte[] data = dataOut.toByteArray();
int lenght = data.length;
dataIn = new ByteArrayInputStream(data);
readStart = System.nanoTime();
in = new ObjectInputStream(dataIn);
in.readObject();
readEnd = System.nanoTime();
Log.d("PARCELTEST", "Serialiazable: " + (writeEnd - writeStart) / 1000
+ "微秒; deSerialiazable: " + (readEnd - readStart) / 1000
+ " 微秒; Size: " + lenght);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
直接上结果:
测试机型:一加5T
021-05-29 19:53:33.404 14297-14297/com.ywj.serialtest D/PARCELTEST: parcel: 123微秒; unparcel: 35微秒; Size: 92
2021-05-29 19:53:33.406 14297-14297/com.ywj.serialtest D/PARCELTEST: Serialiazable: 516微秒; deSerialiazable: 2040 微秒; Size: 103
2021-05-29 19:53:33.965 14297-14297/com.ywj.serialtest D/PARCELTEST: parcel: 127微秒; unparcel: 51微秒; Size: 92
2021-05-29 19:53:33.967 14297-14297/com.ywj.serialtest D/PARCELTEST: Serialiazable: 573微秒; deSerialiazable: 1394 微秒; Size: 103
2021-05-29 19:53:34.420 14297-14297/com.ywj.serialtest D/PARCELTEST: parcel: 142微秒; unparcel: 70微秒; Size: 92
2021-05-29 19:53:34.423 14297-14297/com.ywj.serialtest D/PARCELTEST: Serialiazable: 796微秒; deSerialiazable: 1696 微秒; Size: 103
2021-05-29 19:53:34.967 14297-14297/com.ywj.serialtest D/PARCELTEST: parcel: 117微秒; unparcel: 66微秒; Size: 92
2021-05-29 19:53:34.969 14297-14297/com.ywj.serialtest D/PARCELTEST: Serialiazable: 446微秒; deSerialiazable: 1130 微秒; Size: 103
2021-05-29 19:53:36.130 14297-14297/com.ywj.serialtest D/PARCELTEST: parcel: 135微秒; unparcel: 76微秒; Size: 92
2021-05-29 19:53:36.134 14297-14297/com.ywj.serialtest D/PARCELTEST: Serialiazable: 554微秒; deSerialiazable: 2433 微秒; Size: 103
parcel平均:120微秒 unparcel平均:50微秒 size:92
Serialiazable平均:550微秒 deSerialiazable平均:1500微秒 size:103
Parcelble 速度约为 Serializable 5倍
所以,Parcelble序列化及反序列化比Serializable快?
后来看到了几个网上的测试,表示多层嵌套时,Serializable比 Parcelable 快很多
下一个测试:
构造一颗深度为5,除了叶节点外,每节点有10个子节点的树(即11111个 TreeNode)
public class TreeNode implements Serializable, Parcelable {
private static final long serialVersionUID = 1L;
public List<TreeNode> children;
public String string0;
public String string1;
public String string2;
public int int0;
public int int1;
public int int2;
public boolean boolean0;
public boolean boolean1;
public boolean boolean2;
public TreeNode() {
}
}
11111个TreeNode的结果:
parcel: 120ms unparcel: 22ms size: 1288872
serialize: 30ms deserialize: 30ms size: 614300
1111个TreeNode的结果:
parcel: 30ms unparcel: 7ms size: 128872
serialize: 6ms deserialize: 10ms size: 61615
111个TreeNode的结果:
parcel: 1ms unparcel: <1ms size: 12872
serialize: 4ms deserialize: 7ms size: 6345
为什么数据量大的时候serialize快了?
java.io.ObjectOutputStream#handles 缓存了加载过的 class 信息
总结:
Parcelble:
-
自己读取、写入信息,可用 AS 工具自动生成/升级
-
intent 传递数据一般情况比较快
-
主要在获取CREATOR时使用了反射,用过一次就缓存,后面可复用。
-
在连续的内存空间中存储信息,类描述不复用
-
在Java侧,以K-V方式存储信息,通过Parcel.cpp并写入 C 层。可通过android.os.Parcel#marshall实现持久化但不建议,只适合同一设备的进程通信。
Serializable:
-
实现一个接口即可,也可自定义写入写出方法,优化性能/加密等
-
intent 传输数据相对来说慢5倍。
-
以反射方式获取全量信息,要解析出对象描述,属性描述,产生大量中间变量。
-
类描述可通过写入索引复用
-
存储方式依赖IO,存储、不同设备传输数据可用。
用谁?
Intent 传数据用 Parcelable 效率高,除非有大量的相同类声明。
持久化可以用 Serializable、其它也可考虑Json、protobuf 等。
参考资料:
git 仓库:
第一个测试:
第二个测试: