Android重修课 -- Parcel机制

栏目: Android · 发布时间: 5年前

内容简介:首先澄清上篇文章中一个概念,上篇文章所述的我们来看一眼如果没有自己定义

首先澄清上篇文章中一个概念,上篇文章所述的 ParcelableSerializable 对比,前提是将Parcel机制忽略掉的,我们可以将Parcel机制看成是一种辅助手段。假如 Serializable 的底层也有这么一个高效的辅助 工具 的话,我们是不是就只需要考虑 ParcelablewriteToParcel()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流来将对象进行写和读的处理。

当然,有朋友就指出了 ParcelableSerializable 更高效的原因应该是底层的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的稳定性,因为我们在此处通过自定义的方式来序列化,别的地方是无法知道我们这个序列化过程,也就没有办法反序列化回来了。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

马云如是说

马云如是说

朱甫 / 中国经济出版社 / 2008-1-1 / 39.80元

任何一个企业家的成功,都需要一种特立独行的精神。换尔言之,他一定是不断地否定别人的反对意见,坚持自己独特的观点,才能够真正走向大成功。在中国企业家群像里,马云就是这样一个特立独行的人。 目录 *永不放弃——马云论创业精神 *天下没有难做的生意——马云论经营理念 *B2B时代——马云论电子商务 *网络只是一个工具——马云论互联网与网络公司 *太多钱会坏事——马云论......一起来看看 《马云如是说》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

MD5 加密
MD5 加密

MD5 加密工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具