Binder 主要作用就是进程间通信,而通信离不开数据传递,android.os.Parcel 则是用于在不同的进程或线程之间传递数据的核心基础类。它主要用于在进程间通信(IPC)将数据序列化和反序列化。Parcel 提供了六大基础类型写入读取,还支持读写这些基础类型的数组类型。不仅如此还支持复杂数据类型的读写,如Parcelable(Android 特有)和 Serializable(Java 标准接口)对象。
| 类型名 | 读写占用的内存字节 | |
|---|---|---|
| 六种基本类型及其数组类型 | boolean/boolean[] | 1(数组额外多4字节写数组长度) |
| int/int[] | 4 | |
| char/char[] | 2 | |
| long/long[] | 8 | |
| float/float[] | 4 | |
| double/double[] | 8 | |
| Parcelable | 实现Parcelable接口的类 | 按实际读写的内存大小计算 |
| Serializable | 实现Serializable接口的类 | 按实际读写的内存大小计算 |
| Bundles | Android默认提供一种Key-Value形式打包数据的方法 | 按实际读写的内存大小计算 |
| Binder | 继承自IBinder接口的类对象 | 24(64位系统) |
| FileDescriptor | 文件描述符fd |
详细参考
1、主要功能
- 序列化和反序列化:
Parcel允许将对象序列化到一个流中,并在需要时将其反序列化回来。这对于在进程之间传递对象是非常重要的。 - 数据传输:
Parcel可用于在不同的组件(如 Activity、Service 或其他组件)间传递数据。
2、主要方法
-
写入数据:
writeInt(int value): 将一个整数写入Parcel。writeString(String value): 将一个字符串写入Parcel。writeParcelable(Parcelable value, int flags): 将一个实现了Parcelable接口的对象写入Parcel。
-
读取数据:
readInt(): 从Parcel中读取一个整数。readString(): 从Parcel中读取一个字符串。readParcelable(ClassLoader loader): 从Parcel中读取一个实现了Parcelable接口的对象。
3、核心原理
3.1、Parcel 构造过程
使用基础 obtain 获取一个 Parcel 对象。
Parcel.obtain()
private Parcel(long nativePtr) {
if (DEBUG_RECYCLE) {
mStack = new RuntimeException();
}
//Log.i(TAG, "Initializing obj=0x" + Integer.toHexString(obj), mStack);
init(nativePtr);
}
private void init(long nativePtr) {
if (nativePtr != 0) {
mNativePtr = nativePtr;
mOwnsNativeParcelObject = false;
} else {
mNativePtr = nativeCreate();
mOwnsNativeParcelObject = true;
}
}
Parcel::Parcel()
{
LOG_ALLOC("Parcel %p: constructing", this);
initState();
}
void Parcel::initState()
{
LOG_ALLOC("Parcel %p: initState", this);
mError = NO_ERROR;
mData = nullptr;
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);
mVariantFields.emplace<KernelFields>();
mAllowFds = true;
mDeallocZero = false;
mOwner = nullptr;
mEnforceNoDataAvail = true;
mServiceFuzzing = false;
}
较重要的参数进行解释:
| 参数 | 意义 |
|---|---|
| mData | 表示一块内存,用来存储 IPC 数据,记录的是这块内存缓冲区的内存起始地址 |
| mDataSize | 当前 Parcel 中 mData 已存储的数据大小 |
| mDataCapacity | 当前 Parcel mData 指示的内存总大小 |
| mDataPos | 当前 Parcel 数据写入或读取的内存位置 |
| mVariantFields | Binder 中 Parcel 的优化,通过使用 variant 数据结构来优化空间利用 |
| mVariantFields.mObjects | 表示一块内存(该对象主要用作Binder记录相关对象的引用) |
| mVariantFields.mObjectsSize | 当前Parcel中mObjects已存储的数据大小 |
| mVariantFields.mObjectsCapacity | 当前Parcel mObjects指示的内存的大小 |
3.2、Parcel 写入过程
3.2.1、writeInt
以写一个Int值为例
android.os.Parcel data = android.os.Parcel.obtain();
data.writeInt(1);
frameworks/native/libs/binder/Parcel.cpp
status_t Parcel::writeInt32(int32_t val)
{
return writeAligned(val);
}
3.2.2、writeAligned
frameworks/native/libs/binder/Parcel.cpp
template<class T>
status_t Parcel::writeAligned(T val) {
static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));//函数参数的一些基本检查,确保类型 T 的大小不需要额外的填充
static_assert(std::is_trivially_copyable_v<T>);//确保类型 T 是平凡可拷贝的(trivially copyable)
//检查 Parcel 的当前写入位置 mDataPos 是否有足够的空间来存储 val
//mDataCapacity 是 Parcel 数据缓冲区的总容量
if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
//如果有足够的空间,使用 memcpy 将 val 的内存拷贝到 mData 缓冲区中的当前位置 mDataPos
memcpy(mData + mDataPos, &val, sizeof(val));
//调用 finishWrite 函数来完成写入操作,并传递写入的字节数(sizeof(val))
return finishWrite(sizeof(val));
}
//如果空间不足,调用 growData 函数来扩展数据缓冲区的大小
//growData 的参数是需要额外的字节数(sizeof(val))
status_t err = growData(sizeof(val));
if (err == NO_ERROR) goto restart_write;
return err;
}
3.2.3、finishWrite
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;
}
3.3、Parcel 内存扩容
3.3.1、growData
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;
}
//几种内存溢出的检查
if (len > SIZE_MAX - mDataSize) return NO_MEMORY; // overflow
if (mDataSize + len > SIZE_MAX / 3) return NO_MEMORY; // overflow
//进行扩容获取一个新的内存大小值,新的缓冲区大小是当前大小加上新增长度后总和的1.5倍
size_t newSize = ((mDataSize+len)*3)/2;
return (newSize <= mDataSize)
? (status_t) NO_MEMORY
: continueWrite(std::max(newSize, (size_t) 128));
}
3.3.1、continueWrite
status_t Parcel::continueWrite(size_t desired)
{
// 检查请求的大小是否超出 INT32_MAX如果超出,返回 BAD_VALUE 错误
if (desired > INT32_MAX) {
return BAD_VALUE;
}
// 获取与 kernelFields 或 RPC 相关的字段
auto* kernelFields = maybeKernelFields();
auto* rpcFields = maybeRpcFields();
// 如果目标大小小于当前数据大小,则需要调整对象大小,进行内存裁剪
size_t objectsSize =
kernelFields ? kernelFields->mObjectsSize : rpcFields->mObjectPositions.size();
if (desired < mDataSize) {
if (desired == 0) {
// 如果目标大小为 0,所有对象都将被丢弃
objectsSize = 0;
} else {
// 如果有内核字段,调整对象大小以适应新数据大小
if (kernelFields) {
while (objectsSize > 0) {
// 检查每个对象的位置是否小于目标大小如果是,则停止调整
if (kernelFields->mObjects[objectsSize - 1] < desired) break;
objectsSize--;
}
} else {
// 如果是 RPC 字段,调整对象大小以适应新数据大小
while (objectsSize > 0) {
if (rpcFields->mObjectPositions[objectsSize - 1] < desired) break;
objectsSize--;
}
}
}
}
// 如果存在数据所有者(mOwner)有效
if (mOwner) {
// 如果目标大小为 0,释放当前数据
if (desired == 0) {
freeData();
return NO_ERROR;
}
// 如果存在所有者,需要接管数据的所有权
uint8_t* data = (uint8_t*)malloc(desired); // 使用 malloc 分配新的数据缓冲区
if (!data) {
mError = NO_MEMORY;
return NO_MEMORY;
}
binder_size_t* objects = nullptr;
// 如果有内核字段且需要保留对象,分配新的对象数组并拷贝对象引用
if (kernelFields && objectsSize) {
objects = (binder_size_t*)calloc(objectsSize, sizeof(binder_size_t));
if (!objects) {
free(data); // 如果对象数组分配失败,释放已分配的数据缓冲区
mError = NO_MEMORY;
return NO_MEMORY;
}
// 保留将要保留的对象的引用
size_t oldObjectsSize = kernelFields->mObjectsSize;
kernelFields->mObjectsSize = objectsSize;
acquireObjects(); // 获取对象的引用
kernelFields->mObjectsSize = oldObjectsSize;
}
// 如果是 RPC 字段,截断不需要的 RPC 对象
if (rpcFields) {
if (status_t status = truncateRpcObjects(objectsSize); status != OK) {
free(data);
return status;
}
}
// 将已有的数据复制到新分配的缓冲区中
if (mData) {
memcpy(data, mData, mDataSize < desired ? mDataSize : desired);
}
if (objects && kernelFields && kernelFields->mObjects) {
memcpy(objects, kernelFields->mObjects, objectsSize * sizeof(binder_size_t));
}
// 关闭文件描述符(TODO: 可能需要进一步确认)
if (kernelFields) {
closeFileDescriptors();
}
// 释放所有者的数据,并将所有权转移到当前对象
mOwner(mData, mDataSize, kernelFields ? kernelFields->mObjects : nullptr,
kernelFields ? kernelFields->mObjectsSize : 0);
mOwner = nullptr;
// 更新全局统计信息,并设置新的数据缓冲区及其容量
LOG_ALLOC("Parcel %p: taking ownership of %zu capacity", this, desired);
gParcelGlobalAllocSize += desired;
gParcelGlobalAllocCount++;
mData = data;
mDataSize = (mDataSize < desired) ? mDataSize : desired; // 设定数据实际大小
ALOGV("continueWrite Setting data size of %p to %zu", this, mDataSize);
mDataCapacity = desired; // 更新数据缓冲区容量
if (kernelFields) {
kernelFields->mObjects = objects;
kernelFields->mObjectsSize = kernelFields->mObjectsCapacity = objectsSize;
kernelFields->mNextObjectHint = 0;
kernelFields->mObjectsSorted = false;
}
} else if (mData) {
// 如果没有所有者但已有数据,处理缓冲区的重新分配
if (kernelFields && objectsSize < kernelFields->mObjectsSize) {
#ifdef BINDER_WITH_KERNEL_IPC
// 释放丢弃的对象引用
const sp<ProcessState> proc(ProcessState::self());
for (size_t i = objectsSize; i < kernelFields->mObjectsSize; i++) {
const flat_binder_object* flat =
reinterpret_cast<flat_binder_object*>(mData + kernelFields->mObjects[i]);
if (flat->hdr.type == BINDER_TYPE_FD) {
kernelFields->mFdsKnown = false;
}
release_object(proc, *flat, this);
}
// 重新分配对象数组
if (objectsSize == 0) {
free(kernelFields->mObjects);
kernelFields->mObjects = nullptr;
kernelFields->mObjectsCapacity = 0;
} else {
binder_size_t* objects =
(binder_size_t*)realloc(kernelFields->mObjects,
objectsSize * sizeof(binder_size_t));
if (objects) {
kernelFields->mObjects = objects;
kernelFields->mObjectsCapacity = objectsSize;
}
}
kernelFields->mObjectsSize = objectsSize;
kernelFields->mNextObjectHint = 0;
kernelFields->mObjectsSorted = false;
#else // BINDER_WITH_KERNEL_IPC
LOG_ALWAYS_FATAL("Non-zero numObjects for RPC Parcel");
#endif // BINDER_WITH_KERNEL_IPC
}
// 如果是 RPC 字段,截断 RPC 对象
if (rpcFields) {
if (status_t status = truncateRpcObjects(objectsSize); status != OK) {
return status;
}
}
// 如果已有数据,使用 realloc 调整缓冲区大小
if (desired > mDataCapacity) {
uint8_t* data = reallocZeroFree(mData, mDataCapacity, desired, mDeallocZero);
if (data) {
LOG_ALLOC("Parcel %p: continue from %zu to %zu capacity", this, mDataCapacity,
desired);
gParcelGlobalAllocSize += desired;
gParcelGlobalAllocSize -= mDataCapacity;
mData = data;
mDataCapacity = desired;
} else {
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 {
// 如果没有数据和所有者,分配并初始化数据缓冲区
uint8_t* data = (uint8_t*)malloc(desired);
if (!data) {
mError = NO_MEMORY;
return NO_MEMORY;
}
// 记录当前状态,并初始化数据
if (!(mDataCapacity == 0 &&
(kernelFields == nullptr ||
(kernelFields->mObjects == nullptr && kernelFields->mObjectsCapacity == 0)))) {
ALOGE("continueWrite: %zu/%p/%zu/%zu", mDataCapacity,
kernelFields ? kernelFields->mObjects : nullptr,
kernelFields ? kernelFields->mObjectsCapacity : 0, desired);
}
// 更新全局统计信息,分配新的数据缓冲区
LOG_ALLOC("Parcel %p: allocating with %zu capacity", this, desired);
gParcelGlobalAllocSize += desired;
gParcelGlobalAllocCount++;
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;
}
continueWrite 函数主要用于:
- 扩展缓冲区以容纳更多数据
- 缩小缓冲区以释放不再需要的内存
- 在首次写入数据时进行初始化
分别使用 malloc 和 calloc 的原由
uint8_t* data = (uint8_t*)malloc(desired):用于分配一个指定大小的内存块 desired。这里使用 malloc 是因为我们仅需要一个新的内存块来存储数据,且在分配后会用现有的数据初始化这个内存块(即用 memcpy 进行初始化)。因此,初始内容不重要,直接使用 malloc 即可 uint8_t* data = (uint8_t*)malloc(desired):同样地,用于分配初始数据缓冲区。如果没有现有的数据需要复制和初始化,这里使用 malloc 也是合理的,因为我们会直接用分配的内存初始化数据
objects = (binder_size_t*)calloc(objectsSize, sizeof(binder_size_t)):这里使用 calloc 是因为需要分配并初始化一个对象数组。objects 数组需要被初始化为零,因为在后续操作中,这些零值用于表示未使用的对象位置,或者作为一个起始值以避免未定义行为。使用 calloc 可以确保这些位置初始为零
总结
malloc 用于分配内存时,当你需要的内存块只需要后续初始化(如用 memcpy),且不需要内存块初始为零时使用
calloc 用于分配内存并需要其初始化为零的情况,如需要确保所有的内存位置从一开始就是零值