Android 中parcelable 和 parcel

来源:互联网 发布:网络用词鸡汤什么意思 编辑:程序博客网 时间:2024/05/16 08:51

文章出处:http://blog.csdn.net/shift_wwx

请转载的朋友标明出处~~


前言 :之前一直在用这两个类,但是没有细细的研究,偶尔在网上看到了相关的解释,感觉不是很详细,这里小结一下,如果还有什么遗漏希望有朋友能提出来。


一、parcel

首先说一下parcel,因为parcelable核心也是依赖parcel的。

java中serialize机制,是能将数据对象存入字节流中,在需要的时候重新生成对象,主要应用是利用外部存储设备保存对象状态,以及通过网络传输对象等。

在Android系统中parcel设计主要是在IPC中通信,将对象存储在内存中,这样效率更高,定位为轻量级的高效的对象序列化和反序列化机制。


(以下路径是在android 4.4中,其他版本的路径可能有点变化,但是应该不大,Parcel毕竟是公共中的公共模块。)

frameworks/base/core/java/android/os/Parcel.java:

public final class Parcel {// final class    ......        /**     * Retrieve a new Parcel object from the pool.     */    public static Parcel obtain() {        final Parcel[] pool = sOwnedPool;        synchronized (pool) {            Parcel p;            for (int i=0; i<POOL_SIZE; i++) {                p = pool[i];                if (p != null) {                    pool[i] = null;                    if (DEBUG_RECYCLE) {                        p.mStack = new RuntimeException();                    }                    return p;                }            }        }        return new Parcel(0);    }        ......        /**     * Put a Parcel object back into the pool.  You must not touch     * the object after this call.     */    public final void recycle() {        if (DEBUG_RECYCLE) mStack = null;        freeBuffer();        final Parcel[] pool;        if (mOwnsNativeParcelObject) {            pool = sOwnedPool;        } else {            mNativePtr = 0;            pool = sHolderPool;        }        synchronized (pool) {            for (int i=0; i<POOL_SIZE; i++) {                if (pool[i] == null) {                    pool[i] = this;                    return;                }            }        }    }        ......        /**     * Returns the total amount of data contained in the parcel.     */    public final int dataSize() {        return nativeDataSize(mNativePtr);    }        /**     * Returns the amount of data remaining to be read from the     * parcel.  That is, {@link #dataSize}-{@link #dataPosition}.     */    public final int dataAvail() {        return nativeDataAvail(mNativePtr);    }        /**     * Returns the current position in the parcel data.  Never     * more than {@link #dataSize}.     */    public final int dataPosition() {        return nativeDataPosition(mNativePtr);    }        ......        /**     * Write an integer value into the parcel at the current dataPosition(),     * growing dataCapacity() if needed.     */    public final void writeInt(int val) {        nativeWriteInt(mNativePtr, val);    }    /**     * Write a long integer value into the parcel at the current dataPosition(),     * growing dataCapacity() if needed.     */    public final void writeLong(long val) {        nativeWriteLong(mNativePtr, val);    }        ......}
详细的code就不贴出来了,可以查看source code。

1、obtain()
获取一个新的Parcel对象

2、recycle()

清空、回收Parcel对象的内存

3、dataSize()

返回Parcel中所有数据在用的内存

4、dataAvail()

返回Parcel剩下没有读取的data的空间,dataSize - dataPosition

5、dataPosition()

获取Parcel对象数据的偏移

6、dataCapacity()

返回Parcel对象占用的总的内存大小,这个区别于dataSize是所有数据用的内存,还有多余的内存用于re-allocate

7、setDataSize(int)

字节为单位

8、setDataPosition(int)

0 ~ dataSize()

9、setDataCapacity(int)

字节为单位

10、writeInt(int)、writeLong(long)、writeFloat(float)、writeDouble(double)、writeString(string)、writeCharSequence(CharSequence)、writeByte(byte)、writeStrongBinder(IBinder)、writeStrongInterface(IInterface)、writeFileDescriptor(FileDescriptor)

对应的是read**

11、writeValue(Object)、readValue(ClassLoader)

12、Parcel(int nativePtr)

构造函数:

    private Parcel(int nativePtr) {        if (DEBUG_RECYCLE) {            mStack = new RuntimeException();        }        //Log.i(TAG, "Initializing obj=0x" + Integer.toHexString(obj), mStack);        init(nativePtr);    }    private void init(int nativePtr) {        if (nativePtr != 0) {            mNativePtr = nativePtr;            mOwnsNativeParcelObject = false;        } else {            mNativePtr = nativeCreate();            mOwnsNativeParcelObject = true;        }    }
不管是wirteInt还是什么都有mNativePtr,就是这里来的,刚新建的Parcel应该是需要nativeCreate的。

上面的接口都是native的接口,来看一下(太多了,找writeInt为例吧):

frameworks/base/core/jni/android_os_Parcel.cpp:

{"nativeWriteInt",            "(II)V", (void*)android_os_Parcel_writeInt},
static void android_os_Parcel_writeInt(JNIEnv* env, jclass clazz, jint 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类的writeInt32:

frameworks/native/libs/binder/Parcel.cpp:

status_t Parcel::writeInt32(int32_t val){    return writeAligned(val);}
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;}
1、PAD_SIZE

#define PAD_SIZE(s) (((s)+3)&~3)
读写时4个字节对齐

2、mDataPos+sizeof(val)) <= mDataCapacity
当小于Parcel的总的大小时候,是可以直接write的。

但是如果不小,那么就需要重新计算大小,然后write

status_t Parcel::growData(size_t len){    size_t newSize = ((mDataSize+len)*3)/2;    return (newSize <= mDataSize)            ? (status_t) NO_MEMORY            : continueWrite(newSize);}
mDataSizeSize会多分配50%的大小
continueWrite就是对这些多分配的空间进行初始化、判断,malloc好后进行memcpy,这样的内存操作效率会比Java 序列化中使用外部存储器会高很多。

3、finishWrite(size_t)

status_t Parcel::finishWrite(size_t len){    //printf("Finish write of %d\n", len);    mDataPos += len;    ALOGV("finishWrite Setting data pos of %p to %d\n", this, mDataPos);    if (mDataPos > mDataSize) {        mDataSize = mDataPos;        ALOGV("finishWrite Setting data size of %p to %d\n", this, mDataSize);    }    //printf("New pos=%d, size=%d\n", mDataPos, mDataSize);    return NO_ERROR;}
Parcel中data的pos进行偏移,mDataPos记录了当前data最后一次存放的位置。
4、*reinterpret_cast<T*>(mData+mDataPos) = val;

*(mData+mDataPos)指的就是Parcel中data区的指针,将val存进去就可以。

上面就是writeInt的整个过程了。


再来看一下readInt:

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;    }}
对比writeInt和readInt你会发现,mDataPos会一直往后,也就是说write的顺序是什么,read的顺序也必须是一样的。


二、Parcelable

之前讲过序列化的意义,在IPC间、网络间等传递对象,我们遇到的不但Intent中会用到,AIDL中也会用到。Parcelable接口相对Java 本身的Serializable稍微复杂了一点,但是效率高,c/c++中完全是对内存直接操作。


1、选择序列化方法的原则:

1)在使用内存的时候,Parcelable比Serializable性能高,所以推荐使用Parcelable。
2)Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC。
3)Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的持续性在外界有变化的情况下。尽管Serializable效率低点,但此时还是建议使用Serializable 。


2、Parcelable接口定义

/** * Interface for classes whose instances can be written to * and restored from a {@link Parcel}.  Classes implementing the Parcelable * interface must also have a static field called <code>CREATOR</code>, which * is an object implementing the {@link Parcelable.Creator Parcelable.Creator} * interface.
这里说是在用到Parcel进行一些存储的时候,需要用到这个Parcelable 接口。在implements这个Parcelable接口的时候,必须同时顶一个static的变量CREATOR,类型是Parcelable.Creator。

来看一下详细的Parcelable接口:

public interface Parcelable {    /**     * Flag for use with {@link #writeToParcel}: the object being written     * is a return value, that is the result of a function such as     * "<code>Parcelable someFunction()</code>",     * "<code>void someFunction(out Parcelable)</code>", or     * "<code>void someFunction(inout Parcelable)</code>".  Some implementations     * may want to release resources at this point.     */    public static final int PARCELABLE_WRITE_RETURN_VALUE = 0x0001;        /**     * Bit masks for use with {@link #describeContents}: each bit represents a     * kind of object considered to have potential special significance when     * marshalled.     */    public static final int CONTENTS_FILE_DESCRIPTOR = 0x0001;        /**     * Describe the kinds of special objects contained in this Parcelable's     * marshalled representation.     *       * @return a bitmask indicating the set of special object types marshalled     * by the Parcelable.     */    public int describeContents();        /**     * Flatten this object in to a Parcel.     *      * @param dest The Parcel in which the object should be written.     * @param flags Additional flags about how the object should be written.     * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.     */    public void writeToParcel(Parcel dest, int flags);    /**     * Interface that must be implemented and provided as a public CREATOR     * field that generates instances of your Parcelable class from a Parcel.     */    public interface Creator<T> {        /**         * Create a new instance of the Parcelable class, instantiating it         * from the given Parcel whose data had previously been written by         * {@link Parcelable#writeToParcel Parcelable.writeToParcel()}.         *          * @param source The Parcel to read the object's data from.         * @return Returns a new instance of the Parcelable class.         */        public T createFromParcel(Parcel source);                /**         * Create a new array of the Parcelable class.         *          * @param size Size of the array.         * @return Returns an array of the Parcelable class, with every entry         * initialized to null.         */        public T[] newArray(int size);    }    /**     * Specialization of {@link Creator} that allows you to receive the     * ClassLoader the object is being created in.     */    public interface ClassLoaderCreator<T> extends Creator<T> {        /**         * Create a new instance of the Parcelable class, instantiating it         * from the given Parcel whose data had previously been written by         * {@link Parcelable#writeToParcel Parcelable.writeToParcel()} and         * using the given ClassLoader.         *         * @param source The Parcel to read the object's data from.         * @param loader The ClassLoader that this object is being created in.         * @return Returns a new instance of the Parcelable class.         */        public T createFromParcel(Parcel source, ClassLoader loader);    }}
1)public static final int PARCELABLE_WRITE_RETURN_VALUE = 0x0001; //writeToParcel相关操作的时候可能会用到的一个flag,下面会揭示
和public static final int CONTENTS_FILE_DESCRIPTOR = 0x0001; //marshall的时候用到,每个bit代表一个特殊的意义

这两个是不需要重写的

2)public int describeContents();

默认return 0; 就可以,具体做什么有待进一步研究

3)public void writeToParcel(Parcel dest, int flags);

这个是需要实现的,第一个参数是需要write的Parcel,另一个参数是额外的flag,0 或者 之前的flag  PARCELABLE_WRITE_RETURN_VALUE

4)public interface Creator<T>

这是必须要的,可以创建一个或者多个Parcelable实例,函数createFromParcel的参数Parcel就是之前writeToParcel中的Parcel。

5)public interface ClassLoaderCreator<T> extends Creator<T> {

继承Creator,函数createFromParcel的第一个参数跟Creator是一样的,第二个参数带了ClassLoader,说明可以反序列化不同ClassLoader中的对象。


三、实例一个

public class MyParcelable implements Parcelable {     private int mData;     public int describeContents() //直接返回0     {         return 0;     }     public void writeToParcel(Parcel out, int flags) //需要存储的Parcel     {         out.writeInt(mData);     }     public static final Parcelable.Creator<MyParcelable> CREATOR = new Parcelable.Creator<MyParcelable>() //必须是public static final 类型     {         public MyParcelable createFromParcel(Parcel in) //读出之前writeToParcel中存储的Parcel,可以通过构造函数分别read出来,进行初始化         {             return new MyParcelable(in);         }         public MyParcelable[] newArray(int size)          {             return new MyParcelable[size];         }     };          private MyParcelable(Parcel in)//读出来进行初始化     {         mData = in.readInt();     } }
请看code中的注释。



0 0