内容简介:首先澄清上篇文章中一个概念,上篇文章所述的我们来看一眼如果没有自己定义
首先澄清上篇文章中一个概念,上篇文章所述的 Parcelable
比 Serializable
对比,前提是将Parcel机制忽略掉的,我们可以将Parcel机制看成是一种辅助手段。假如 Serializable
的底层也有这么一个高效的辅助 工具 的话,我们是不是就只需要考虑 Parcelable
的 writeToParcel()
、 createFromParcel()
和对 Serializabl
e的 writeObject()
、 readObject()
的操作对比了呢。而我们在实现 Parcelable
的时候,是必须要手动去实现这两个方法的。 Serializable
就不需要,但是代价就是效率要低一些。我所说的是这一个层面的比较。当然,只是这个层面的比较是非常片面的,那我们继续往底层看看。
看看Serializable底层序列化的原理
我们来看一眼 ObjectOutputStream
中的 writeObject()
方法
public final void writeObject(Object obj) throws IOException {
if (enableOverride) {
writeObjectOverride(obj);
return;
}
try {
writeObject0(obj, false);
} catch (IOException ex) {
if (depth == 0) {
try {
writeFatalException(ex);
} catch (IOException ex2) {
}
exceptions during writeObject().
}
throw ex;
}
}
复制代码
如果没有自己定义 writeObject()
的方法,将会调用 writeObject0()
方法,我们继续往里面看看。
private void writeObject0(Object obj, boolean unshared) throws IOException {
// ...省略代码
// remaining cases
// BEGIN Android-changed: Make Class and ObjectStreamClass replaceable.
if (obj instanceof Class) {
writeClass((Class) obj, unshared);
} else if (obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
// END Android-changed: Make Class and ObjectStreamClass replaceable.
} 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(obj, desc, unshared);
} else {
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
}
复制代码
在这个方法中,具体逻辑就是使用反射去构造类的对象,对类的类型判断然后进行相应处理。
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 {
desc.checkSerialize();
bout.writeByte(TC_OBJECT);
writeClassDesc(desc, false);
handles.assign(unshared ? null : obj);
if (desc.isExternalizable() && !desc.isProxy()) {
writeExternalData((Externalizable) obj);
} else {
writeSerialData(obj, desc);
}
} finally {
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
}
复制代码
看到这里大概也就知道了 Serializable
的底层原理就是通过通过操作IO流来将对象进行写和读的处理。
当然,有朋友就指出了 Parcelable
比 Serializable
更高效的原因应该是底层的Parcel机制。讲的非常对,Android中的Parcel机制就是对应的Serializable底层的操作IO流的操作。那么为什么有了Parcel机制,Parcelable就比Serializable能高效十来倍呢?
那么这篇文章,我们一起来探讨一下Android中的Parcel机制。
还是从我们实现Parcelable说起。
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.userId);
dest.writeString(this.name);
dest.writeInt(this.age);
}
protected UserInfo(Parcel in) {
this.userId = in.readString();
this.name = in.readString();
this.age = in.readInt();
}
@Override
public UserInfo createFromParcel(Parcel source) {
return new UserInfo(source);
}
复制代码
这是我们实现Parcelable代码中的片段,我们可以看到我们自己手动去实现的 writeToParcel()
和 createFromParcel()
都是交由Parcel去处理读和写了。
看看Parcel的读和写的原理
我们就拎出一个 writeInt()
方法进去看看,可以看到实际上是调用了jni中的 nativeWriteInt
方法。那么我们就去看看在jni中的android_os_Parcel 对应的实际调用应该是:
static void android_os_Parcel_writeInt(JNIEnv* env, jclass clazz, jlong nativePtr, jint val) {
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
const status_t err = parcel->writeInt32(val);
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
}
复制代码
调用的是Parcel.cpp中的writeInt32()方法
status_t Parcel::writeInt32(int32_t val)
{
return writeAligned(val);
}
复制代码
在Parcel.cpp内部调用 writeAligned()
方法
template<class T>
status_t Parcel::writeAligned(T val) {
COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T)) == sizeof(T));
if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
*reinterpret_cast<T*>(mData+mDataPos) = val;
return finishWrite(sizeof(val));
}
status_t err = growData(sizeof(val));
if (err == NO_ERROR) goto restart_write;
return err;
}
复制代码
这里就是将我们需要存储的数据写到内存中的具体实现。其他的writeXXX()方法也跟此类似。
对应的反序列化操作
readInt()
方法流程跟 writeInt()
类型,最终调用的就是Parcel.cpp中的 readAligned()
方法
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 += sizeof(T);
*pArg = *reinterpret_cast<const T*>(data);
return NO_ERROR;
} else {
return NOT_ENOUGH_DATA;
}
}
复制代码
这里也就是在反序列化的时候,获取到之前存储的数据,将数据一一还原。
Parcel机制的实现逻辑
Parcel.cpp
大概的实现逻辑就是在初始化的时候,开辟了一块内存空间,然后我们在序列化对象的时候,控制position的位置把数据通过Parcel的writeXXX()方法一段一段的写入到这块内存中,整个过程是连续的。所以在我们反序列化的时候也是控制position一段一段取出内存中数据,在通过Parcel的readXXX()方法还原成对象中的数据。这两个过程writeXXX()和readXXX()的顺序必须是一样的,这样才能保证序列化和反序列化的成功。
总结两者效率差别的真正原因
看到这里,我们心中就有个大概的概念了,Parcel机制实际上就是通过共享内存的方法,实现序列化和反序列化。而Serializable是通过操作IO流来读写对象。所以来说,这才是真正的为什么说Parcelable比Serializable高效十来倍的原因。
如果说Serializable通过自己去实现writeObject()和readObject(),也使用这种内存共享的手段,效率是不会比Parcelable差的。但如果这样做,我们就失去了Serializable的稳定性,因为我们在此处通过自定义的方式来序列化,别的地方是无法知道我们这个序列化过程,也就没有办法反序列化回来了。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 重修决策树:信息增益与Gini系数
- 快速失败机制 & 失败安全机制
- JavaScript线程机制与事件机制
- 区块链是怎样将分布式组网机制、合约机制、共识机制等技术结合并应用
- Java内存机制和GC回收机制-----笔记
- javascript垃圾回收机制 - 标记清除法/引用计数/V8机制
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
MySQL必知必会
[英] Ben Forta / 刘晓霞、钟鸣 / 人民邮电出版社 / 2009-1 / 39.00元
《MySQL必知必会》MySQL是世界上最受欢迎的数据库管理系统之一。书中从介绍简单的数据检索开始,逐步深入一些复杂的内容,包括联结的使用、子查询、正则表达式和基于全文本的搜索、存储过程、游标、触发器、表约束,等等。通过重点突出的章节,条理清晰、系统而扼要地讲述了读者应该掌握的知识,使他们不经意间立刻功力大增。一起来看看 《MySQL必知必会》 这本书的介绍吧!