前言
这篇文章是笔者在学习Serializable和Parcelable两处知识点针对自己疑问去探索的学习纪录
一、笔者产生的问题
为什么Java实现了Serializable对象才能意识到这是支持序列化的?
Serializable为什么效率上不如Parcelable?
二、源码解读
Serializable相关
众所周知,Serializable是个空接口,要进行序列化与反序列化的类实现这个接口之后,主要通过ObjectInputStream和ObjectOutputStream进行序列化与反序列化,如果没有实现Serializable或者其他相关接口则会报错。
那Serializable的源码几近空空如也,我们不妨从ObjectInputStream和ObjectOutputStream源码进行下手解读
先来看ObjectOutputStream的源码
先把使用的代码贴上,方便后续阅读
try {
//输出序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//输入反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
person = (Person) ois.readObject();
return person;
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
return null;
}
源码入口:
/**
* Creates an ObjectOutputStream that writes to the specified OutputStream.
* This constructor writes the serialization stream header to the
* underlying stream; callers may wish to flush the stream immediately to
* ensure that constructors for receiving ObjectInputStreams will not block
* when reading the header.
*
* <p>If a security manager is installed, this constructor will check for
* the "enableSubclassImplementation" SerializablePermission when invoked
* directly or indirectly by the constructor of a subclass which overrides
* the ObjectOutputStream.putFields or ObjectOutputStream.writeUnshared
* methods.
*
* @param out output stream to write to
* @throws IOException if an I/O error occurs while writing stream header
* @throws SecurityException if untrusted subclass illegally overrides
* security-sensitive methods
* @throws NullPointerException if {@code out} is {@code null}
* @since 1.4
* @see ObjectOutputStream#ObjectOutputStream()
* @see ObjectOutputStream#putFields()
* @see ObjectInputStream#ObjectInputStream(InputStream)
*/
public ObjectOutputStream(OutputStream out) throws IOException {
verifySubclass();
// bout表示底层的字节数据容器
bout = new BlockDataOutputStream(out);
handles = new HandleTable(10, (float) 3.00);
subs = new ReplaceTable(10, (float) 3.00);
enableOverride = false;
writeStreamHeader(); // 写入文件头
bout.setBlockDataMode(true);// flush数据
if (extendedDebugInfo) {
debugInfoStack = new DebugTraceInfoStack();
} else {
debugInfoStack = null;
}
}
补:常见的ByteArrayOutputStream和FileOutputStream都是继承自OutputStream,所以传入前面的两个流作为参数的构造函数源码便是以上呈现的。
这个构造函数首先将out对象绑定到bout
/**
* Creates new BlockDataOutputStream on top of given underlying stream.
* Block data mode is turned off by default.
*/
BlockDataOutputStream(OutputStream out) {
this.out = out;
dout = new DataOutputStream(this);
}
然后调用writeStreamHeader()方法
/**
* The writeStreamHeader method is provided so subclasses can append or
* prepend their own header to the stream. It writes the magic number and
* version to the stream.
*
* @throws IOException if I/O errors occur while writing to the underlying
* stream
*/
protected void writeStreamHeader() throws IOException {
bout.writeShort(STREAM_MAGIC);
bout.writeShort(STREAM_VERSION);
}
/**
* Magic number that is written to the stream header.
*/
final static short STREAM_MAGIC = (short)0xaced;
/**
* Version number that is written to the stream header.
*/
final static short STREAM_VERSION = 5;
接下来调用writeObject()
public final void writeObject(Object obj) throws IOException {
if (enableOverride) {
writeObjectOverride(obj);
return;
}
try {
writeObject0(obj, false);
} catch (IOException ex) {
if (depth == 0) {
writeFatalException(ex);
}
throw ex;
}
}
很明显看到我们之前的代码里初始化的时候enableOverride会被设置成false,所以这里会调用writeObject0方法,我们继续往下看
/**
* Underlying writeObject/writeUnshared implementation.
*/
private void writeObject0(Object obj, boolean unshared)
throws IOException
{
boolean oldMode = bout.setBlockDataMode(false);
depth++;
try {
// handle previously written and non-replaceable objects
int h;
if ((obj = subs.lookup(obj)) == null) {
writeNull();
return;
} else if (!unshared && (h = handles.lookup(obj)) != -1) {
writeHandle(h);
return;
} else if (obj instanceof Class) {
writeClass((Class) obj, unshared);
return;
} else if (obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
return;
}
// check for replacement object
Object orig = obj;
//获取要进行序列化的对象
Class<?> cl = obj.getClass();
ObjectStreamClass desc;
for (;;) {
// REMIND: skip this check for strings/arrays?
//检验了是否有重写入的方法
Class<?> repCl;
desc = ObjectStreamClass.lookup(cl, true);
if (!desc.hasWriteReplaceMethod() ||
(obj = desc.invokeWriteReplace(obj)) == null ||
(repCl = obj.getClass()) == cl)
{
break;
}
cl = repCl;
}
if (enableReplace) {
Object rep = replaceObject(obj);
if (rep != obj && rep != null) {
cl = rep.getClass();
desc = ObjectStreamClass.lookup(cl, true);
}
obj = rep;
}
// if object replaced, run through original checks a second time
if (obj != orig) {
subs.assign(orig, obj);
if (obj == null) {
writeNull();
return;
} else if (!unshared && (h = handles.lookup(obj)) != -1) {
writeHandle(h);
return;
} else if (obj instanceof Class) {
writeClass((Class) obj, unshared);
return;
} else if (obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
return;
}
}
// remaining cases
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) {
//实现了Serializable接口的类,进行以下的方法
writeOrdinaryObject(obj, desc, unshared);
} else {
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
} finally {
depth--;
bout.setBlockDataMode(oldMode);
}
}
嚯,总归是看到我们分析的主角了,从这里我们就可以看到,如果没有实现Serializable方法,那么便不会调用writeOrdinaryObject方法,而是会往下抛出异常
接下来看看writeOrdinaryObject()方法
/**
* Writes representation of an "ordinary" (i.e., not a String, Class,
* ObjectStreamClass, array, or enum constant) serializable object to the
* stream.
*/
private void writeOrdinaryObject(Object obj,
ObjectStreamClass desc,
boolean unshared)
throws IOException
{
if (extendedDebugInfo) {
debugInfoStack.push(
(depth == 1 ? "root " : "") + "object (class \"" +
obj.getClass().getName() + "\", " + obj.toString() + ")");
}
try {
// 写入Object标志位
desc.checkSerialize();
bout.writeByte(TC_OBJECT);
// 写入类元数据
writeClassDesc(desc, false);
handles.assign(unshared ? null : obj);
if (desc.isRecord()) {
writeRecordData(obj, desc);
} else if (desc.isExternalizable() && !desc.isProxy()) {
writeExternalData((Externalizable) obj);// 写入被序列化的对象的实例数据
} else {
writeSerialData(obj, desc);
}
} finally {
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
}
/**
* new Object.
*/
final static byte TC_OBJECT = (byte)0x73;
接下来会调用writeClassDesc()方法写入被序列化对象的类的类元数据,writeClassDesc()方法实现如下:
private void writeClassDesc(ObjectStreamClass desc, boolean unshared)
throws IOException
{
int handle;
if (desc == null) {
// 如果desc为null
writeNull();
} else if (!unshared && (handle = handles.lookup(desc)) != -1) {
writeHandle(handle);
} else if (desc.isProxy()) {
writeProxyDesc(desc, unshared);
} else {
writeNonProxyDesc(desc, unshared);
}
}
一般情况下接下来会调用writeNonProxyDesc()方法,该方法实现如下:
private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared)
throws IOException
{
// TC_CLASSDESC = (byte)0x72;
// 表示一个新的Class描述符
bout.writeByte(TC_CLASSDESC);
handles.assign(unshared ? null : desc);
if (protocol == PROTOCOL_VERSION_1) {
// do not invoke class descriptor write hook with old protocol
desc.writeNonProxy(this);
} else {
writeClassDescriptor(desc);
}
Class cl = desc.forClass();
bout.setBlockDataMode(true);
if (cl != null && isCustomSubclass()) {
ReflectUtil.checkPackageAccess(cl);
}
annotateClass(cl);
bout.setBlockDataMode(false);
bout.writeByte(TC_ENDBLOCKDATA);
writeClassDesc(desc.getSuperDesc(), false);
}
writeNonProxyDesc调用writeNonProxy()最终把数据完成写入啦
void writeNonProxy(ObjectOutputStream out) throws IOException {
out.writeUTF(name); // 写入类的名字
out.writeLong(getSerialVersionUID()); // 写入类的序列号
byte flags = 0;
// 获取类的标识
if (externalizable) {
flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
int protocol = out.getProtocolVersion();
if (protocol != ObjectStreamConstants.PROTOCOL_VERSION_1) {
flags |= ObjectStreamConstants.SC_BLOCK_DATA;
}
} else if (serializable) {
flags |= ObjectStreamConstants.SC_SERIALIZABLE;
}
if (hasWriteObjectData) {
flags |= ObjectStreamConstants.SC_WRITE_METHOD;
}
if (isEnum) {
flags |= ObjectStreamConstants.SC_ENUM;
}
out.writeByte(flags); // 写入类的flag
out.writeShort(fields.length); // 写入对象的字段的个数
for (int i = 0; i < fields.length; i++) {
ObjectStreamField f = fields[i];
out.writeByte(f.getTypeCode());
out.writeUTF(f.getName());
if (!f.isPrimitive()) {
// 如果不是原始类型,即是对象或者Interface
// 则会写入表示对象或者类的类型字符串
out.writeTypeString(f.getTypeString());
}
}
}
从这里我们也可以看到,Serilizable的工作几乎都是在Java层完成的,输入输出也是采用的流的形式,产生了大量的I/O操作,过程中产生了大量的对象产生和消除,这必然会引起GC机制降低效率,并且用到了反射机制,这一定程度上也降低了其的效率。
Parcelable相关
Parcelable与Serializable相关,只是它是针对连续内存进行操作
Parcelable严格意义上来说是Android SDK中的一个API,它的序列化是基于Native层进行实现的,不同版本的API可能实现方式有所不同
先来看看Parcelable的源码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package android.os;
public interface Parcelable {
int CONTENTS_FILE_DESCRIPTOR = 1;
int PARCELABLE_WRITE_RETURN_VALUE = 1;
int describeContents();
void writeToParcel(Parcel var1, int var2);
public interface Creator<T> {
T createFromParcel(Parcel var1);
T[] newArray(int var1);
}
public interface ClassLoaderCreator<T> extends Parcelable.Creator<T> {
T createFromParcel(Parcel var1, ClassLoader var2);
}
}
然后我们发现,其实比较关键的两个函数都有Parcel的身影,似乎很多工作都是Parcel完成的,Parcelable只是一个壳子,于是我们继续看Parcel,这里我们看一个writeInt方法
/**
* Write an integer value into the parcel at the current dataPosition(),
* growing dataCapacity() if needed.
*/
public final void writeInt(int val) {
int err = nativeWriteInt(mNativePtr, val);
if (err != OK) {
nativeSignalExceptionForError(err);
}
}
@CriticalNative
private static native int nativeWriteInt(long nativePtr, int val);
发现是个Native方法,okk,看Native层
笔者定位到了android_os_Parcel.cpp,原谅我没找到具体C的实现,这里借鉴一位大佬的博客给出思路
static void android_os_Parcel_writeInt(JNIEnv* env, jclass clazz, jlong nativePtr, jint val) {
//通过指针再解释强转成Parcel对象
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
//最终还是调用的Parcel中的writeInt32函数
const status_t err = parcel->writeInt32(val);
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
}
}
...
//实际调用的是一个通用的模版方法
status_t Parcel::writeInt32(int32_t val)
{
return writeAligned(val);
}
...
//模版方法
//其中
//mData表示指向Parcel内存的首地址
//mDataPos表示指向Parcel空闲内存的首地址
//mDataCapacity表示Parcel分配内存的大小
template<class T>
status_t Parcel::writeAligned(T val) {
COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T)) == sizeof(T));
//先判断加上val后会不会超过可用大小
if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
//reinterpret_cast是c++的再解释强制转换操作
//首先会计算mData + mDataPos得到物理地址,转成指向T类型的指针(T类型就是实际入参的类型)
//然后将val赋值给指针指向的内容
*reinterpret_cast<T*>(mData+mDataPos) = val;
//主要逻辑是修改mDataPos的偏移地址
//将偏移地址加上新增加的数据的字节数
return finishWrite(sizeof(val));
}
//如果超过了可用大小,执行增长函数
//之后再goto到上面的restart_write标签执行写入逻辑
status_t err = growData(sizeof(val));
if (err == NO_ERROR) goto restart_write;
return err;
}
通过上述代码分析,writeInt32函数调用一个模板方法,然后将数据写入到一段共享的内存中
读过程
template<class T>
status_t Parcel::readAligned(T *pArg) const {
COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T)) == sizeof(T));
if ((mDataPos+sizeof(T)) <= mDataSize) {
//获取读取数据的地址
const void* data = mData+mDataPos;
//mDataPos指向下一个数据
mDataPos += sizeof(T);
//根据数据指针类型取出数据
*pArg = *reinterpret_cast<const T*>(data);
return NO_ERROR;
} else {
return NOT_ENOUGH_DATA;
}
}
读写起始地址必须一致,这也是我们为什么要保持读写类的成员变量一致的原因
因为是采用写入动态内存的方法,所以如果要保证数据持久化的、避免因为突发状况(like 断电)产生不可预计的错误,尽量还是使用Serializable
但是因为Parcelable是对内存直接将进行读写的,且直接使用指针操作,所以效率上会快很多,也适用于专注于效率的场合,例如进程间小数据的通信
总结
前面笔者提出的疑问已经在源码解答部分给出了解释,这里就不赘述了,觉得博客还可以的朋友点个赞吧
参考文章
Android 序列化(Serializable和Parcelable)
Android Parcelable原理分析
Serializable 和Parcelable 的区别(Android每日面试题)
Java对象序列化底层原理源码解析WhatHowWhyOther