Android系统方面那些事

Parcel类详解

2018-08-09  本文已影响0人  凯玲之恋

1 Parcel简介

5713484-9123c36de14effb2.png
  • 小型数据:从用户空间(源进程)copy到kernel空间(Binder驱动中)再写回用户空间(目标进程,binder驱动负责寻找目标进程)
  • 大型数据:使用android 的匿名共享内存 (Ashmem)传递
  • binder对象:Kernel binder驱动专门处理

2 Parcel.java类介绍

主要讲解两个部分,一个是类注释,一个是核心方法。

2.1 Parcel.java类注释

2.2 常用方法

2.2.1 Parcel设置相关

母庸质疑,存入的数据越多,Parcel所占用的内存空间越大。

ps:如果写入数据时,系统发现已经超出了Parcel的存储能力,它会自动申请所需要的内存空间,并扩展dataCapacity;并且每次写入都是从dataPosition()开始的。

2.2.2 Primitives

原始类型数据的读写操作。比如:

  • writeInt(int) :写入一个整数
  • writeFloat(float) :写入一个浮点数(单精度)
  • writeDouble(double):写入一个双精度
  • writeString(string):写入一个字符串
  • readInt(int) :读出一个整数
  • readFloat(float) :读出一个浮点数(单精度)
  • readDouble(double):读出一个双精度
  • readString(string):读出一个字符串

2.2.3 Primitives Arrays

  • writeBooleanArray(boolean[]):写入布尔数组
  • readBooleanArray(boolean[]):读出布尔数组
  • boolean[] createBooleanArray():读取并返回一个布尔数组
  • writeByteArray(byte[] ):写入字节数组
  • writeByteArray(byte[],int , int ) 和上面几个不同的是,这个函数最后面的两个参数分别表示数组中需要被写入的数据起点以及需要写入多少。
  • readByteArray(byte[]):读取字节数组
  • byte[] createByteArray():读取并返回一个数组

2.2.4 Parcelables

遵循Parcelable协议的对象可以通过Parcel来存取,如开发人员经常用的的bundle就是实现Parcelable的,与此类对象相关的Parcel操作包括:

  • writeParcelable(Parcelable,int):将这个Parcel类的名字和内容写入Parcel中,实际上它是通过回调此Parcelable的writeToParcel()方法来写入数据的。
  • readParcelable(ClassLoader):读取并返回一个新的Parcelable对象
  • writeParcelableArray(T[],int):写入Parcelable对象数组。
  • readParcelable(ClassLoader):读取并返回一个Parcelable数组对象

2.2.5 Bundle

Bundle继承自Parcelable,是一种特殊的type-safe的容器。Bundle的最大特点是采用键值对的方式存储数据,并在一定程度上优化了读取效率。

  • writeBundle(Bundle):将Bundle写入parcel
  • readBundle():读取并返回一个新的Bundle对象
  • readBundle(ClassLoader):读取并返回一个新的Bundle对象,ClassLoader用于Bundle获取对应的Parcelable对象。

2.2.6 Activity Object

因为传递后的对象仍然会基于和原对象相同的文件流进行操作,因而可以认为是Active Object的一种

2.2.7 Untyped Containers

它用于读写标准的任意类型的java容器。包括:

  • writeArray(Object[])
  • readArray(ClassLoader)
  • writeList(List)
  • readList(List, ClassLoader)
  • readArrayList(ClassLoader)
  • writeMap(Map)
  • readMap(Map, ClassLoader)
  • writeSparseArray(SparseArray)
  • readSparseArray(ClassLoader)

2.2.8 Parcel创建与回收

  • obtain():获取一个Parcel,可以理解new了一个对象,其实是有一个Parcel池
  • recyle():清空,回收Parcel对象的内存

2.2.9 异常的读写

  • writeException():在Parcel队头写入一个异常
  • readException():在Parcel队头读取,若读取值为异常,则抛出该异常;否则程序正常运行

3 创建Parcl对象

3.1 obtain()方法

app可以通过Parcel。obtain()接口来获取一个Parcel对象。

/**
     * Retrieve a new Parcel object from the pool.
     * 从Parcel池中取出一个新的Parcel对象
     */
    public static Parcel obtain() {
        //系统预先产出的一个Parcel池(其实就是一个Parcel数组),大小为6
        final Parcel[] pool = sOwnedPool;
        synchronized (pool) {
            Parcel p;
            for (int i=0; i<POOL_SIZE; i++) {
                p = pool[i];
                if (p != null) {
                    //引用置为空,这样下次就知道这个Parcel已经被占用了
                    pool[i] = null;
                    if (DEBUG_RECYCLE) {
                        p.mStack = new RuntimeException();
                    }
                    return p;
                }
            }
        }
        //如果Parcel池中已经空,就直接新建一个。
        return new Parcel(0);
    }

在这里,我们要注意到这里的池其实是一个数组,从里面提取对象的时候,从头扫描到尾,找不到为null的手,直接new一个Parcle对象并返回。

3.2 Parcel构造函数

private Parcel(long nativePtr) {
        if (DEBUG_RECYCLE) {
            mStack = new RuntimeException();
        }
        //Log.i(TAG, "Initializing obj=0x" + Integer.toHexString(obj), mStack);
        init(nativePtr);
    }

通过上面代码,我们知道,构造函数里面什么都没做,只是调用了init()函数,注意传入的是nativePtr是0

private void init(long nativePtr) {
        if (nativePtr != 0) {
             //如果nativePtr不是0
            mNativePtr = nativePtr;
            mOwnsNativeParcelObject = false;
        } else {
            //如果nativePtr是0
            // nativeCreate() 为本地层代码准备的指针
            mNativePtr = nativeCreate();
            mOwnsNativeParcelObject = true;
        }
    }

  private long mNativePtr; // used by native code
  private static native long nativeCreate();

3.3 nativeCreate()方法

Android跨进程通信IPC之4——关于"JNI"的那些事
我们得知nativeCreate对应的是JNI层的/frameworks/base/core/jni中,实际上Parcel.java只是一个简单的中介,最终所有类型的读写操作都是通过本地代码实现的:
android_os_Parcel.cpp

//frameworks/base/core/jni/android_os_Parcel.cpp    551行
static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz)
{
    Parcel* parcel = new Parcel();
    return reinterpret_cast<jlong>(parcel);
}

mNativePtr变量实际上是一个本底层的Parcel(C++)对象
Parcel-Native的构造函数
代码在Parcel.cpp 343行

Parcel::Parcel()
{
    LOG_ALLOC("Parcel %p: constructing", this);
    initState();
}

构造函数很简单就是调用了 initState() 函数,让我们来看下 initState()函数。
代码在Parcel.cpp 1901行

void Parcel::initState()
{
    LOG_ALLOC("Parcel %p: initState", this);
    mError = NO_ERROR;
    mData = 0;
    mDataSize = 0;
    mDataCapacity = 0;
    mDataPos = 0;
    ALOGV("initState Setting data size of %p to %zu", this, mDataSize);
    ALOGV("initState Setting data pos of %p to %zu", this, mDataPos);
    mObjects = NULL;
    mObjectsSize = 0;
    mObjectsCapacity = 0;
    mNextObjectHint = 0;
    mHasFds = false;
    mFdsKnown = true;
    mAllowFds = true;
    mOwner = NULL;
    mOpenAshmemSize = 0;
}

初始话很简单,几乎都是初始化为 0(NULL) 的

3.4 Parcel.h:

其实每一个Parcel对象都有一个Native对象相对应(以后均简称Parcel-Native对象,而Parcel对象均指Java层),该Native对象就是实际的写入读出的一个对象,java端对它的引用是上面mNativePtr

对应的Native层的Parcel定义是在 /frameworks/native/inlcude/binder/Parcel.h

class Parcel {
public:
    ...
    int32_t             readInt32() const; // 举个例子
    ...
    status_t            writeInt32(int32_t val);
    ...
private:
    uint8_t*            mData;//数据存储的起始指针
    size_t              mDataSize;//总数据大小
    size_t              mDataCapacity;//总空间 (包括已用和可用)大小,这个空间是变长的
    mutable size_t      mDataPos;//当前数据可写入的内存其实位置
}

3.5 总结:

使用Parcel一般是通过Parcel.obtain()从对象池中获取一个新的Parcel对象,如果对象池中没有则直接new的Parcel则直接创建新的一个Parcel对象,并且会自动创建一个Parcel-Native对象

4 Parcell对象回收

4.1 Parcel-Native对象的回收

// Parcel.java
    @Override
    protected void finalize() throws Throwable {
        if (DEBUG_RECYCLE) {
            if (mStack != null) {
                Log.w(TAG, "Client did not call Parcel.recycle()", mStack);
            }
        }
        destroy();
    }

    private void destroy() {
        if (mNativePtr != 0) {
            if (mOwnsNativeParcelObject) {
                nativeDestroy(mNativePtr);
                updateNativeSize(0);
            }
            mNativePtr = 0;
        }
    }

    private static native void nativeDestroy(long nativePtr);

在finalize()方法里面调用了 destroy()方法,而在destroy()方法里面调用了nativeDestroy(long)方法

//frameworks/base/core/jni/android_os_Parcel.cpp      567行
static void android_os_Parcel_destroy(JNIEnv* env, jclass clazz, jlong nativePtr)
{
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    delete parcel;//直接delete了 parcel对象
}

4.2 recycle()方法

/**
     * Put a Parcel object back into the pool.  You must not touch
     * the object after this call.
     * 将Parcel对象放回池中。 这次调用之后,你就无法继续联系他了
     */
    public final void recycle() {
        if (DEBUG_RECYCLE) mStack = null;
        //Parcel-Native 对象的数据清空
        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;
                }
            }
        }
    }

加入对象池之后的对象除非重新靠obtain()启动,否则不能直接使用,因为它时刻都可能被其他地方获取使用导致数据错误。

5 存储空间

flat_binder_object

/*
 * This is the flattened representation of a Binder object for transfer
 * between processes.  The 'offsets' supplied as part of a binder transaction
 * contains offsets into the data where these structures occur.  The Binder
 * driver takes care of re-writing the structure type and data as it moves
 * between processes.
 */
struct flat_binder_object {
    struct binder_object_header hdr;
    __u32               flags;

    /* 8 bytes of data. */
    union {
        binder_uintptr_t    binder; /* local object */
        __u32           handle; /* remote object */
    };
    /* extra data associated with local object */
    binder_uintptr_t    cookie;
};
  • 跨进程的时候:flat_binder_object的type为BINDER_TYPE_HANDLE跨进程的时候
  • 非跨进程的时候:flat_binder_object的type为BINDER_TYPE_BINDER

6 关于偏移量

那么Parcel内部存储机制是怎么样的?偏移量又是什么东西?

6.1 基本类型

类型 bit数量 字节
boolean 1 bit 1字节
char 16bit 2字节
int 32bit 4字节
long 64 bit 8 字节
float 32 bit 4 字节
double 64bit 8字节
  • 实际存放字节:
    辨别一:32bit (<=32bit) 例如:boolean,char,int
    辨别二:实际占用字节(>32bit) 例如:long,float,String 等
  • 实际读取字节:
    辨别一:32bit (<=32bit) 例如:boolean,char,int
    辨别二:实际占用字节(>32bit) 例如:long,float,String 组等

所以,当我们写入/读取一个数据时,偏移量至少为4byte(32bit),于是,偏移量的公式如下:

f(x)= 4x (x=0,1,....n)

事实上,我们可以通过setDataPosition(int position)来直接操作我们欲读取数据时的偏移量。毫无疑问,你可以设置任何便宜量,但是所读取值的类型是有误的。因此设置便宜量读取值的时候,需要小心。

6.2 注意事项

在writeXXX()和readXXX()时,导致的偏移量时共用的,例如我们在writeIn(23)后,此时的dataposition=4,如果我想读取它,简单的通过readInt()是不行的,只能得到0,这时我们只能通过setDataPosition(0)设置为起始偏移量,从起始位置读取四个字节,即可得23。因此,在读取某个值时,需要使用setDataPosition(int)使偏移量偏移到我们的指定位置。

6.3 取值规范

由于可能存在读取值的偏差,一个默认的取值规范为:

  • 1、读取复杂对象时:对象匹配时,返回当前偏移位置的对象;对象不匹配时,返回null
  • 2、读取简单对象时:对象匹配时,返回当前偏移位置的对象:对象不匹配时,返回0

6.4 存放空间图

5713484-d0960176652b250f.png

7 Int类型数据写入

以writeInt()为例进行数据写入的跟踪
时序图如下:


5713484-177c06edd517c83f.jpeg

7.1 Parcel.writeInt(int)

/**
     * Write an integer value into the parcel at the current dataPosition(),
     * growing dataCapacity() if needed.
     */
    public final void writeInt(int val) {
        nativeWriteInt(mNativePtr, val);
    }

7.2 (2) android_os_Parcel_writeInt(JNIEnv*,jclass,jlong,jint)函数

代码在android_os_Parcel.cpp 233行

static void android_os_Parcel_writeInt(JNIEnv* env, jclass clazz, jlong nativePtr, jint val) {
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        const status_t err = parcel->writeInt32(val);
        if (err != NO_ERROR) {
            signalExceptionForError(env, clazz, err);
        }
    }
}

调用的 Parcel-Native类的writeInt32(jint)函数

7.3 Parcel::writeInt32(int32_t val)函数

代码在[Parcel.cpp]http://androidxref.com/6.0.1_r10/xref/frameworks/native/libs/binder/Parcel.cpp) 748行

status_t Parcel::writeInt32(int32_t val)
{
    return writeAligned(val);
}

我们看到实际上是调用的 Parcel-Native类的writeAligned()函数

7.4 Parcel::writeAligned(T val)函数

代码在Parcel.cpp 1148行

template<class T>
status_t Parcel::writeAligned(T val) {
    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
    if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
        //分支二
        *reinterpret_cast<T*>(mData+mDataPos) = val;
        return finishWrite(sizeof(val));
    }
    //分支一  刚刚创建Parcel-Native对象
    status_t err = growData(sizeof(val));
    if (err == NO_ERROR) goto restart_write;
    return err;
}

7.5 Parcel::growData(size_t len)函数

代码在Parcel.cpp 1683行

status_t Parcel::growData(size_t len)
{
     //如果超过int的最大值
    if (len > INT32_MAX) {
        // don't accept size_t values which may have come from an
        // inadvertent conversion from a negative int.
        return BAD_VALUE;
    }

    size_t newSize = ((mDataSize+len)*3)/2;
    //通过上面的代码newSize一般来说都会大于mDataSize
    return (newSize <= mDataSize)
            ? (status_t) NO_MEMORY
            : continueWrite(newSize);
}

PS:这里是parcel的增长算法,((mDataSize+len)3/2);*带有一定预测性的增长,避免频繁的空间调整(每次调整都需要重新malloc内存的,频繁的话会影响效率)。然后这里有个newSize< mDataSize就认为NO_MEMORY。这是如果溢出了(是负数),就认为申请不到内存了。

7.6 Parcel::continueWrite(size_t desired)函数

代码在Parcel.cpp 1743行

status_t Parcel::continueWrite(size_t desired)
{
    if (desired > INT32_MAX) {
        // don't accept size_t values which may have come from an
        // inadvertent conversion from a negative int.
        return BAD_VALUE;
    }

    // If shrinking, first adjust for any objects that appear
    // after the new data size.
    size_t objectsSize = mObjectsSize;
    if (desired < mDataSize) {
        if (desired == 0) {
            objectsSize = 0;
        } else {
            while (objectsSize > 0) {
                if (mObjects[objectsSize-1] < desired)
                    break;
                objectsSize--;
            }
        }
    }
    //分支一
    if (mOwner) {
        // If the size is going to zero, just release the owner's data.
        if (desired == 0) {
            freeData();
            return NO_ERROR;
        }

        // If there is a different owner, we need to take
        // posession.
        uint8_t* data = (uint8_t*)malloc(desired);
        if (!data) {
            mError = NO_MEMORY;
            return NO_MEMORY;
        }
        binder_size_t* objects = NULL;

        if (objectsSize) {
            objects = (binder_size_t*)calloc(objectsSize, sizeof(binder_size_t));
            if (!objects) {
                free(data);

                mError = NO_MEMORY;
                return NO_MEMORY;
            }

            // Little hack to only acquire references on objects
            // we will be keeping.
            size_t oldObjectsSize = mObjectsSize;
            mObjectsSize = objectsSize;
            acquireObjects();
            mObjectsSize = oldObjectsSize;
        }

        if (mData) {
            memcpy(data, mData, mDataSize < desired ? mDataSize : desired);
        }
        if (objects && mObjects) {
            memcpy(objects, mObjects, objectsSize*sizeof(binder_size_t));
        }
        //ALOGI("Freeing data ref of %p (pid=%d)", this, getpid());
        mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie);
        mOwner = NULL;

        LOG_ALLOC("Parcel %p: taking ownership of %zu capacity", this, desired);
        pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
        gParcelGlobalAllocSize += desired;
        gParcelGlobalAllocCount++;
        pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);

        mData = data;
        mObjects = objects;
        mDataSize = (mDataSize < desired) ? mDataSize : desired;
        ALOGV("continueWrite Setting data size of %p to %zu", this, mDataSize);
        mDataCapacity = desired;
        mObjectsSize = mObjectsCapacity = objectsSize;
        mNextObjectHint = 0;
    //分支二 
    } else if (mData) {
        if (objectsSize < mObjectsSize) {
            // Need to release refs on any objects we are dropping.
            const sp<ProcessState> proc(ProcessState::self());
            for (size_t i=objectsSize; i<mObjectsSize; i++) {
                const flat_binder_object* flat
                    = reinterpret_cast<flat_binder_object*>(mData+mObjects[i]);
                if (flat->type == BINDER_TYPE_FD) {
                    // will need to rescan because we may have lopped off the only FDs
                    mFdsKnown = false;
                }
                release_object(proc, *flat, this, &mOpenAshmemSize);
            }
            binder_size_t* objects =
                (binder_size_t*)realloc(mObjects, objectsSize*sizeof(binder_size_t));
            if (objects) {
                mObjects = objects;
            }
            mObjectsSize = objectsSize;
            mNextObjectHint = 0;
        }

        // We own the data, so we can just do a realloc().
        if (desired > mDataCapacity) {
            uint8_t* data = (uint8_t*)realloc(mData, desired);
            if (data) {
                LOG_ALLOC("Parcel %p: continue from %zu to %zu capacity", this, mDataCapacity,
                        desired);
                pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
                gParcelGlobalAllocSize += desired;
                gParcelGlobalAllocSize -= mDataCapacity;
                pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
                mData = data;
                mDataCapacity = desired;
            } else if (desired > mDataCapacity) {
                mError = NO_MEMORY;
                return NO_MEMORY;
            }
        } else {
            if (mDataSize > desired) {
                mDataSize = desired;
                ALOGV("continueWrite Setting data size of %p to %zu", this, mDataSize);
            }
            if (mDataPos > desired) {
                mDataPos = desired;
                ALOGV("continueWrite Setting data pos of %p to %zu", this, mDataPos);
            }
        }
    //分支三
    } else {
        // This is the first data.  Easy!
        uint8_t* data = (uint8_t*)malloc(desired);
        if (!data) {
            mError = NO_MEMORY;
            return NO_MEMORY;
        }

        if(!(mDataCapacity == 0 && mObjects == NULL
             && mObjectsCapacity == 0)) {
            ALOGE("continueWrite: %zu/%p/%zu/%zu", mDataCapacity, mObjects, mObjectsCapacity, desired);
        }

        LOG_ALLOC("Parcel %p: allocating with %zu capacity", this, desired);
        pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
        gParcelGlobalAllocSize += desired;
        gParcelGlobalAllocCount++;
        pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);

        mData = data;
        mDataSize = mDataPos = 0;
        ALOGV("continueWrite Setting data size of %p to %zu", this, mDataSize);
        ALOGV("continueWrite Setting data pos of %p to %zu", this, mDataPos);
        mDataCapacity = desired;
    }
    return NO_ERROR;
}
  • 分支一:如果设置了release函数指针(即mOwener),调用release函数进行处理
  • 分支二:没有设置release函数指针,但是mData中存在数据,需要在原来的数据的基础上扩展存储空间。
  • 分支三:没有设置release函数指针,并且mData不存在数据(就是注释上说的第一次使用),调用malloc申请内存块,保存mData。设置相应的设置capacity、size、pos、object的值。

这里应该走分支分支三,分支三很简单,主要是调用malloc()方法分配一块(mDataSize+size(val))3/2大小的内存,然后让mData指向该内存,并且将这里可以归纳一下,growData()方法只是分配了一内存

7.7 Parcel::writeAligned(T val)函数 分支二

分支二代码就两行,如下

        *reinterpret_cast<T*>(mData+mDataPos) = val;
        return finishWrite(sizeof(val));
  • 1、reinterpret_cast<T>(mData+mDataPos) = val; 这行代码是直接获取当前地址强制转化指针类型,然后赋值。
  • 2、调用finishWrite()函数

7.8 Parcel::finishWrite(size_t len)函数

代码在Parcel.cpp 642行

status_t Parcel::finishWrite(size_t len)
{
    if (len > INT32_MAX) {
        // don't accept size_t values which may have come from an
        // inadvertent conversion from a negative int.
        return BAD_VALUE;
    }

    //printf("Finish write of %d\n", len);
    mDataPos += len;
    ALOGV("finishWrite Setting data pos of %p to %zu", this, mDataPos);
    if (mDataPos > mDataSize) {
        mDataSize = mDataPos;
        ALOGV("finishWrite Setting data size of %p to %zu", this, mDataSize);
    }
    //printf("New pos=%d, size=%d\n", mDataPos, mDataSize);
    return NO_ERROR;
}

8 Int类型数据读出

以readInt()为例进行数据写入的跟踪
时序图如下:


5713484-bc98aa0511f16523.jpeg

8.1 Parcel.readInt(int)

/**
     * Read an integer value from the parcel at the current dataPosition().
     */
    public final int readInt() {
        return nativeReadInt(mNativePtr);
    }

    private static native int nativeReadInt(long nativePtr);

8.2 android_os_Parcel_readInt(JNIEnv* env, jclass clazz, jlong nativePtr) 函数

代码在android_os_Parcel.cpp 379行

static jint android_os_Parcel_readInt(JNIEnv* env, jclass clazz, jlong nativePtr)
{
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        return parcel->readInt32();
    }
    return 0;
}

8.3 Parcel::readInt32() 函数

代码在Parcel.cpp 1168行

int32_t Parcel::readInt32() const
{
    return readAligned<int32_t>();
}

8.4 readAligned<int32_t>() 函数

代码在Parcel.cpp 1138行

template<class T>
T Parcel::readAligned() const {
    T result;
    if (readAligned(&result) != NO_ERROR) {
        result = 0;
    }
    return result;
}

其实内部是有调用了Parcel::readAligned(T *pArg)函数

注意:Parcel::readAligned(T *pArg)和 Parcel::readAligned()的区别,一个是有入参的,一个是无入参的。

8.5 readAligned<int32_t>() 函数

代码在Parcel.cpp 1124行

template<class T>
status_t Parcel::readAligned(T *pArg) const {
    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(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;
    }

这个就是根据 mData+mDataPos 和具体的类型,进行强制类型转化获取对应的值。

8.6 注意事项:

同进程情况下,数据读取过程跟写入几乎一致,由于使用的是同一个Parcel对象,mDataPos首先要调整到0之后才能读取,同进程数据写入/读取并不会有什么效率提高,仍然会进行内存的考虑和分配,所以一般来说尽量避免同进程使用Parcel传递大数据。

Android跨进程通信IPC之6——Parcel--Binder对象的写入和读出

参考

Android跨进程通信IPC之4——AndroidIPC基础1

上一篇下一篇

猜你喜欢

热点阅读