Android 中parcelable 和 parcel

本文深入探讨了Android中Parcel和Parcelable的工作原理及其实现细节,包括Parcel内部的内存管理机制和Parcelable接口的具体用法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章出处:https://justinwei.blog.youkuaiyun.com/article/details/47122687

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

0. 前言

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

1. 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。

  • obtain()

获取一个新的Parcel对象

  • recycle()

清空、回收Parcel对象的内存

  • dataSize()

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

  • dataAvail()

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

  • dataPosition()

获取Parcel对象数据的偏移

  • dataCapacity()

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

  • setDataSize(int)

字节为单位

  • setDataPosition(int)

0 ~ dataSize()

  • setDataCapacity(int)

字节为单位

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

对应的是read**

  • writeValue(Object)
  • readValue(ClassLoader)
  • Parcel(int nativePtr)

1.1 构造函数

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

    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},
frameworks/base/core/jni/android_os_Parcel.cpp

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;
}
  • PAD_SIZE
#define PAD_SIZE(s) (((s)+3)&~3)

读写时4个字节对齐

  • 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 序列化中使用外部存储器会高很多。

  • 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最后一次存放的位置。

  • *reinterpret_cast<T*>(mData+mDataPos) = val;

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

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

再来看一下readInt():

frameworks/native/libs/binder/Parcel.cpp

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的顺序也必须是一样的。

2. Parcelable

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

2.1 选择序列化方法的原则

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

2.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。

2.3 详细的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);
    }
}
  • 变量 PARCELABLE_WRITE_RETURN_VALUE

writeToParcel() 相关操作的时候可能会用到的一个flag

  • 变量 CONTENTS_FILE_DESCRIPTOR 

marshall的时候用到,每个bit代表一个特殊的意义

  • describeContents()

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

  • writeToParcel(Parcel dest, int flags)

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

  • interface Creator<T>

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

  • public interface ClassLoaderCreator<T> extends Creator<T> 

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

3. 实例

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中的注释。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

私房菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值