内容简介:首先澄清上篇文章中一个概念,上篇文章所述的我们来看一眼如果没有自己定义
首先澄清上篇文章中一个概念,上篇文章所述的 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机制
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。