Parcel的意思是“打包”。在Android中,Parcel的作用类似于Java中的serialize,即对象的序列化。Android系统定位为内存受限的设备,因此对性能要求更高,另外系统中采用了新的IPC机制,必然要求使用性能更出色的对象传输方式。在这样的环境下,Parcel被设计出来,其定位就是轻量级的高效的对象序列化和反序列化机制。
Parcel的最主要作用是在各个activity之间传递数据。Android开发中,很经常在各activity之间传递数据,而跟据Android的设计架构,即使同一个程序中的Activity都不一定运行在同一个进程中,所以处理数据传递时你不能老假设两个activity都运行于同一进程,那么只能按进程间传递数据来处理,使之具有最广泛的适应性。
Parcel的接口主要有以下几类:
一、Parcel的获取与释放
- 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);
- }
-
- public final void recycle() {
- if (DEBUG_RECYCLE) mStack = null;
- freeBuffer();
- final Parcel[] pool = mOwnObject != 0 ? sOwnedPool : sHolderPool;
- synchronized (pool) {
- for (int i=0; i<POOL_SIZE; i++) {
- if (pool[i] == null) {
- pool[i] = this;
- return;
- }
- }
- }
- }
获取接口obtain是从一个已分配的Parcel池中取得一个Parcel,即sOwnedPool,其定义如下:
- private static final int POOL_SIZE = 6;
- private static final Parcel[] sOwnedPool = new Parcel[POOL_SIZE];
- private static final Parcel[] sHolderPool = new Parcel[POOL_SIZE];
当使用完获得的Parcel后,最好使用recycle将其释放回池,以便下次使用,否则导致Parcel池无可分配资源而new新对象。幸运的是,eclipse会给出此类的警告提示。
二、Parcel内部指针的移动
- public final native int dataSize();
- public final native int dataAvail();
- public final native int dataPosition();
- public final native int dataCapacity();
- public final native void setDataSize(int size);
- public final native void setDataPosition(int pos);
- public final native void setDataCapacity(int size);
由于Parcel的读写是直接在内存中的,所以这几个接口就是指示当前的读写指针,类似于Linux下的文件读写操作:
dataSize:返回当前内存块中总共的数据
dataPosition:返回当前读操作指针
dataAvail:返回当前可读的数据,即dataSize - dataPosition
dataCapacity:返回当前内存块的容量(在不重新分配内存的前提下),dataCapacity - dataSize即为可写入的数据量
三、数据读写类
1、序列化与反序列化
public final native byte[] marshall();
public final native void unmarshall(byte[] data, int offest, int length);
作用类似于序列化和反序列化。即将当前Parcel的数据序列化为byte数组,或者将byte数组反序列化到当前Parcel中。
注:unmarshall后,如果要读取数据,首先需要将文件指针移动到初始化位置,即setDataPosition(0)。
2、基本数据类型的读写
private native void writeNative(byte[]
b, int offset, int len);
public final native void writeInt(int val);
public final native void writeLong(long val);
public final native void writeFloat(float val);
public final native void writeDouble(double val);
public final native void writeString(String val);
public final native void writeStrongBinder(IBinder val);
public final native void writeFileDescriptor(FileDescriptor val);
public final native int readInt();
public final native long readLong();
public final native float readFloat();
public final native double readDouble();
public final native String readString();
public final native IBinder readStrongBinder();
全部都是native方法,通过JNI来调用底层实现。其余类型的读写都是在基本数据类型的基础上得来的。如:
- public final void writeByte(byte val) {
- writeInt(val);
- }
- public final byte readByte() {
- return (byte)(readInt() & 0xff);
- }
可见,byte在Parcel中也占用四个字节。
3、Array类型的读写
Array类型的接口按类型来分,如boolean[]/char[]/int[]/float[]/double[]/String[],而每一种类型都包含三个接口:
public final void writeXXXArray(boolean[] val);
public final boolean[] createXXXArray();
public final void readXXXArray(boolean[] val);
以boolean类型为例:其源码如下:
- public final void writeBooleanArray(boolean[] val) {
- if (val != null) {
- int N = val.length;
- writeInt(N);
- for (int i=0; i<N; i++) {
- writeInt(val[i] ? 1 : 0);
- }
- } else {
- writeInt(-1);
- }
- }
-
- public final boolean[] createBooleanArray() {
- int N = readInt();
- // >>2 as a fast divide-by-4 works in the create*Array() functions
- // because dataAvail() will never return a negative number. 4 is
- // the size of a stored boolean in the stream.
- if (N >= 0 && N <= (dataAvail() >> 2)) {
- boolean[] val = new boolean[N];
- for (int i=0; i<N; i++) {
- val[i] = readInt() != 0;
- }
- return val;
- } else {
- return null;
- }
- }
-
- public final void readBooleanArray(boolean[] val) {
- int N = readInt();
- if (N == val.length) {
- for (int i=0; i<N; i++) {
- val[i] = readInt() != 0;
- }
- } else {
- throw new RuntimeException("bad array lengths");
- }
- }
可见,其write操作的过程是先以整形的方式写入Array的长度,然后依次写入Array的每一个元素。而读操作的过程则正好相反。
注:从以上示例看出,boolean类型在Parcel中也占据四个字节。
4、通用接口
这两个接口依赖于前面基本类型的接口,内部通过instanceof关键字类判断数据类型。各个类型常量定义如下:
- private static final int VAL_NULL = -1;
- private static final int VAL_STRING = 0;
- private static final int VAL_INTEGER = 1;
- private static final int VAL_MAP = 2;
- private static final int VAL_BUNDLE = 3;
- private static final int VAL_PARCELABLE = 4;
- private static final int VAL_SHORT = 5;
- private static final int VAL_LONG = 6;
- private static final int VAL_FLOAT = 7;
- private static final int VAL_DOUBLE = 8;
- private static final int VAL_BOOLEAN = 9;
- private static final int VAL_CHARSEQUENCE = 10;
- private static final int VAL_LIST = 11;
- private static final int VAL_SPARSEARRAY = 12;
- private static final int VAL_BYTEARRAY = 13;
- private static final int VAL_STRINGARRAY = 14;
- private static final int VAL_IBINDER = 15;
- private static final int VAL_PARCELABLEARRAY = 16;
- private static final int VAL_OBJECTARRAY = 17;
- private static final int VAL_INTARRAY = 18;
- private static final int VAL_LONGARRAY = 19;
- private static final int VAL_BYTE = 20;
- private static final int VAL_SERIALIZABLE = 21;
- private static final int VAL_SPARSEBOOLEANARRAY = 22;
- private static final int VAL_BOOLEANARRAY = 23;
- private static final int VAL_CHARSEQUENCEARRAY = 24;
|