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的稳定性,因为我们在此处通过自定义的方式来序列化,别的地方是无法知道我们这个序列化过程,也就没有办法反序列化回来了。


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

查看所有标签

猜你喜欢:

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

Reversing

Reversing

艾拉姆(Eilam,E.) / 韩琪、杨艳、王玉英、李娜 / 电子工业出版社 / 2007-9 / 79.00元

本书描述的是在逆向与反逆向之间展开的一场旷日持久的拉锯战。作者Eldad Eilam以一个解说人的身份为我们详尽地评述了双方使用的每一招每一式的优点与不足。 书中包含的主要内容有:操作系统的逆向工程;.NET平台上的逆向工程;逆向未公开的文件格式和网络协议;逆向工程的合法性问题;拷贝保护和数字版权管理技术的逆向工程;防止别人对你的代码实施逆向工程的各种技术;恶意程序的逆向工程;反编译器的基本......一起来看看 《Reversing》 这本书的介绍吧!

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

在线压缩/解压 JS 代码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

html转js在线工具
html转js在线工具

html转js在线工具