欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Adroid读代码 Parcel - (2)

程序员文章站 2022-06-08 23:54:07
...

关键字:Android, Parcel

20180828 tjy

转载请注明出处

前面的内容列表:

Adroid读代码 Parcel - (1) https://www.jianshu.com/p/42d2dc89649e

今天继续看Parcel的几个简单方法,假设在写如Parcel的时候,Parcel是刚创建的对象,里面是空的。

public final void writeInt(int val)
public final void writeString(String val)
public final void writeBoolean(boolean val)
public final void writeStringArray(String[] val)

public final void writeInt(int val)

//from http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/java/android/os/Parcel.java#writeInt

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

//from http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/jni/android_os_Parcel.cpp#785

/*
    接下来进入JNI代码
    nativeWriteInt
    {"nativeWriteInt", "(JI)V", (void*)android_os_Parcel_writeInt},
    调用android_os_Parcel_writeInt方法。
*/
 static void android_os_Parcel_writeInt(JNIEnv* env, 
        jclass clazz, 
        jlong nativePtr, 
        jint val) {
    /*把mNativePtr转换成C++对象指针*/
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        const status_t err = parcel->writeInt32(val);//继续向下调用
        if (err != NO_ERROR) {
            signalExceptionForError(env, clazz, err);
        }
    }
}
/*
    先看下C++中class Parcel的属性的定义
    from http://androidxref.com/8.1.0_r33/xref/frameworks/native/include/binder/Parcel.h
    
    class Parcel
    {
        //mData表示存储数据的内存空间的首地址,
        //初始值为0
        uint8_t* mData;
        //mDataSize表示当前存储的有效数据的大小,
        //初始值为0
        size_t mDataSize;
        //mDataCapacity表示当前Parcel可用空间的容量,
        //初始值为0
        size_t mDataCapacity;
        //mDataPos表示当前写/读的位置,
        //从 mData+mDataPos开始读/写数据,
        //初始值为0
        mutable size_t  mDataPos;
    };
*/
//from http://androidxref.com/8.1.0_r33/xref/frameworks/native/libs/binder/Parcel.cpp#933
/*
    Parcel中用int32_t存储int
*/
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_UNSAFE(sizeof(T)) == sizeof(T));

    if ((mDataPos+sizeof(val)) <= mDataCapacity) {//0+4<=0 不成立
restart_write:
        /*
            goto跳转到这里,空间申请过了,可以写入了。
        */
        *reinterpret_cast<T*>(mData+mDataPos) = val;//写入int32_t
        //调用finishWrite
        return finishWrite(sizeof(val));
    }

    /*
        运行到这里表示没有可用空间存放数据,需要申请空间
        growData会计算需要申请的空间,并申请空间。
        我们在这里是第一次写入数据到Parcel,比较简单。
        growData会返回NO_ERROR,
    */
    status_t err = growData(sizeof(val));
    //申请完空间后,用goto语句返回restart_write继续写入。
    if (err == NO_ERROR) goto restart_write;
    return err;
}

//len = 4
status_t Parcel::growData(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;
    }

    /*
        这里申请空间并不是需要多少申请多少,
        而是大于需要的空间,不是严格的1.5倍。
    */
    //(0+4)*3/2=6
    size_t newSize = ((mDataSize+len)*3)/2;
    /*
        6<0 不成立,调用 continueWrite(newSize)
        continuerite正常会返回 NO_ERROR,
        所以growData会返回NO_ERROR
    */
    return (newSize <= mDataSize)
            ? (status_t) NO_MEMORY
            : continueWrite(newSize);
}

//desired = 6
status_t Parcel::continueWrite(size_t desired)
{
    if (desired > INT32_MAX) {//false
        // 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) {//6<0, false
        if (desired == 0) {
            objectsSize = 0;
        } else {
            while (objectsSize > 0) {
                if (mObjects[objectsSize-1] < desired)
                    break;
                objectsSize--;
            }
        }
    }

    if (mOwner) {//false
        // If the size is going to zero, just release the owner's data.
        if (desired == 0) {//false
            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) {//false
        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 {//true
        //第一次向Parcel中写入数据
        // This is the first data.  Easy!
        //第一次申请空间,这里用的是 malloc
        uint8_t* data = (uint8_t*)malloc(desired);
        if (!data) {
            mError = NO_MEMORY;
            return NO_MEMORY;
        }

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

        LOG_ALLOC("Parcel %p: allocating with %zu capacity", this, desired);
        pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
        //static size_t gParcelGlobalAllocSize = 0;
        //增加分配的内存空间计数
        gParcelGlobalAllocSize += desired;
        gParcelGlobalAllocCount++;
        pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
        //把申请的空间赋值给mData
        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;//=6,Parcel空间容量
    }
    //从这个函数返回NO_ERROR
    return NO_ERROR;
}

//len=4
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
    */
    mDataPos += len;
    ALOGV("finishWrite Setting data pos of %p to %zu", this, mDataPos);
    if (mDataPos > mDataSize) {//4>0成立
        mDataSize = mDataPos;//更新mDataSize
        ALOGV("finishWrite Setting data size of %p to %zu", this, mDataSize);
    }
    //printf("New pos=%d, size=%d\n", mDataPos, mDataSize);
    return NO_ERROR;
}

上面是第一次写入一个int,接下来写入一个string。

public final void writeString(String val)

//from http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/java/android/os/Parcel.java

/**
* Write a string value into the parcel at the current dataPosition(),
* growing dataCapacity() if needed.
*/
public final void writeString(String val) {
    mReadWriteHelper.writeString(this, val);
}

//from http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/java/android/os/Parcel.java#352

/**
* Called when writing a string to a parcel. Subclasses wanting to write a string
* must use {@link #writeStringNoHelper(String)} to avoid
* infinity recursive calls.
*/
public void writeString(Parcel p, String s) {
    nativeWriteString(p.mNativePtr, s);
}

//from androidxref.com/8.1.0_r33/xref/frameworks/base/core/jni/android_os_Parcel.cpp#792
 {"nativeWriteString",         "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeString},
 
 //from androidxref.com/8.1.0_r33/xref/frameworks/base/core/jni/android_os_Parcel.cpp#276
 
static void android_os_Parcel_writeString(JNIEnv* env, jclass clazz, jlong nativePtr, jstring val)
{
    //转换成对象指针
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        status_t err = NO_MEMORY;
        if (val) {
            //转换成 jchar*
            const jchar* str = env->GetStringCritical(val, 0);
            if (str) {
                //调用 writeString16,第二个参数是字符串长度,即jchar的个数
                //第一个参数把jchar*转成了char16_t
                err = parcel->writeString16(
                    reinterpret_cast<const char16_t*>(str),
                    env->GetStringLength(val));
                //需要release str
                env->ReleaseStringCritical(val, str);
            }
        } else {
            err = parcel->writeString16(NULL, 0);
        }
        if (err != NO_ERROR) {
            signalExceptionForError(env, clazz, err);
        }
    }
}

//from androidxref.com/8.1.0_r33/xref/frameworks/native/libs/binder/Parcel.cpp#1063
status_t Parcel::writeString16(const char16_t* str, size_t len)
{
    //字符串为空则写入-1
    if (str == NULL) return writeInt32(-1);
    //先写入int表示字符串长度
    status_t err = writeInt32(len);
    if (err == NO_ERROR) {
        //由于原来的len用的是 char8_t,这里要写入char16_t,
        //所以写入的len要乘以sizeof(char16_t),
        //其实这样写比较容易理解:len *= sizeof(char16_t)/sizeof(char8_t)
        len *= sizeof(char16_t);
        //writeInplace是先占下len+sizeof(char16_t)空间,这里并不写入数据
        //返回的是开始写入的起始地址。
        //等等,为什么是 len+sizeof(char16_t)?不是len?
        //下面有答案
        uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t));
        if (data) {
            //用memcpy函数去写char16_t*串,占用的字节数是len。
            memcpy(data, str, len);
            //前面多占了一个char16_t,这里写入0表示结束
            *reinterpret_cast<char16_t*>(data+len) = 0;
            return NO_ERROR;
        }
        err = mError;
    }
    return err;
}

//from androidxref.com/8.1.0_r33/xref/frameworks/native/libs/binder/Parcel.cpp#719
void* Parcel::writeInplace(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 NULL;
    }
    //这里是4个字节对齐,不够补齐
    const size_t padded = pad_size(len);

    // sanity check for integer overflow
    if (mDataPos+padded < mDataPos) {
        return NULL;
    }

    //先检查空间是否足够,
    //上次写入int的时候申请了6个字节的空间,写入int消耗了4个,剩下2个
    //刚刚在写入字符串长度的时候需要写入int,需要4个字节,空间不够
    //会申请(4+4)*3/2=12个字节,
    //写入int后剩下8个字节
    //假如这次我们要写入"hello",需要的空间是 sizeof("hello")*sizeof(char16_t) + sizeof(char16_t) = 12,12个字节已经对齐了。
    //空间不够,看代码。
    if ((mDataPos+padded) <= mDataCapacity) {//false
restart_write:
        //在下面扩充完空间通过goto语句返回到这里,此时空间足够了。
        //printf("Writing %ld bytes, padded to %ld\n", len, padded);
        uint8_t* const data = mData+mDataPos;//data指向开始写入的位置。

        // Need to pad at end?
        if (padded != len) {//false
#if BYTE_ORDER == BIG_ENDIAN
            static const uint32_t mask[4] = {
                0x00000000, 0xffffff00, 0xffff0000, 0xff000000
            };
#endif
#if BYTE_ORDER == LITTLE_ENDIAN
            static const uint32_t mask[4] = {
                0x00000000, 0x00ffffff, 0x0000ffff, 0x000000ff
            };
#endif
            //printf("Applying pad mask: %p to %p\n", (void*)mask[padded-len],
            //    *reinterpret_cast<void**>(data+padded-4));
            *reinterpret_cast<uint32_t*>(data+padded-4) &= mask[padded-len];
        }
        /*
            接下来调用 finishWrite更新mDataPos和mDataSize,就不把这个函数抄在这里了。
        */
        finishWrite(padded);
        /*
            返回要写入的起始位置。回到上面调用这个函数的位置。
        */
        return data;
    }
    //growData重新扩充空间,返回NO_ERROR
    //goto 到 restart_write
    status_t err = growData(padded);
    if (err == NO_ERROR) goto restart_write;
    return NULL;
}

//from androidxref.com/8.1.0_r33/xref/frameworks/native/libs/binder/Parcel.cpp#2471
status_t Parcel::growData(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;
    }
    /*
        mDataSize此此时等于8(两个int)
        len等于12
        所以,newSize = (8+12)*3/2=30
    */
    size_t newSize = ((mDataSize+len)*3)/2;
    //接下来调用 continueWrite,会返回NO_ERROR,
    //这个函数也会返回 NO_ERROR
    //会到上面调用这个函数的地方
    return (newSize <= mDataSize)
            ? (status_t) NO_MEMORY
            : continueWrite(newSize);
}

//from androidxref.com/8.1.0_r33/xref/frameworks/native/libs/binder/Parcel.cpp#2534
//desired = 30
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 < mDa taSize) {//false
        if (desired == 0) {
            objectsSize = 0;
        } else {
            while (objectsSize > 0) {
                if (mObjects[objectsSize-1] < desired)
                    break;
                objectsSize--;
            }
        }
    }

    if (mOwner) {//false
        // 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) {//true
        if (objectsSize < mObjectsSize) {//false
            // 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) {//true, 30 > 12
            //调用realloc扩充内存空间,原来已经写入的数据不会变
            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;//mData重新指向空间起始位置
                mDataCapacity = desired;//更新mDataCapacity = 30
            } 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;
    }
    //最后返回 NO_ERROR, 回到上面调用这个函数的地方。
    return NO_ERROR;
}

通过上面的代码,在写入一个java int的时候,会调用C++的实现去做。
先判断Parcel的空间是否足够,不够的话就去申请足够的空间(比需要的空间多),第一次申请空间用的是malloc函数,后面因为空间不够重新申请空间就会使用realloc,realloc可以使一块内存扩大或缩小,这里是扩大内存,原始的内存里面的数据还会存在。
申请空间完成后,向新的空间中继续写入。

写入java string的时候,先写入string长度,在把string转换成char16_t*写入Parcel,外加一个char16_t 0.

writeBoolean函数会把boolean转换成int, true=1, false=0.
writeStringArray函数会先写入一个表示字符串数组长度的int,然后再把string挨个写入。

这几个是典型的基本数据类型写入Parcel,从Parcel中读取基本数据类型就是上面的逆过程。

注释里面说了,Parcel主要是为IPC的目的做的,所以重点和难点在于写入IBinder对象。由于涉及到IBinder,建议先看过IBinder后再来看写入对象。事实上,看IBinder的过程中,会有向Parcel读写IBinder对象的过程,那个时候再回到这里来看。

转载于:https://www.jianshu.com/p/ea2edf56827f