关键字: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对象的过程,那个时候再回到这里来看。