前文中曾经遇到过Parcel,从命名上知道他负责数据打包。在checkService的请求/响应体系中,Parcel只打包了基本数据类型,如Int32、String16……后面还要用于打包抽象数据类型flat_binder_object,这会稍微复杂一些,因此有必要拿出来单独研究。我们从Parcel::writeInterfaceToken(…)追起,它的层层调用关系如下,这些函数都在frameworks/native/libs/binder/Parcel.cpp文件中,行数和函数名为:

    writeInterfaceToken(…)
Parcel::writeInt32(int32_t val)
Parcel::writeAligned(val)

所有的基本数据类型的打包最后都由writeAligned(…)实现的,其内部逻辑也非常简单,

frameworks/native/libs/binder/Parcel.cpp:1149

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; // 将val追加到mData
return finishWrite(sizeof(val));
} status_t err = growData(sizeof(val)); // 如果mData空间不够,则先扩容
if (err == NO_ERROR) goto restart_write;
return err;
}

mData是一块内存栈,writeXXX则把数据写入栈,如果mData空间不够,先给mData扩容,并把原先的数据搬到新的空间,再把新数据写入栈。

Parcel::writeStrongBinder(…)的逻辑更复杂一些,它的调用关系如下:

frameworks/native/libs/binder/Parcel.cpp

    Parcel::writeStrongBinder(const sp<IBinder>& val)
Parcel::flatten_binder(const sp<ProcessState>& /*proc*/, const sp<IBinder>& binder =val, Parcel* out=this)

来看flatten_binder(…),frameworks/native/libs/binder/Parcel.cpp:205

status_t flatten_binder(const sp<ProcessState>& /*proc*/,
const sp<IBinder>& binder, Parcel* out)
{
flat_binder_object obj; obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
if (binder != NULL) {
IBinder *local = binder->localBinder();
if (!local) { // remote类型的binder封装逻辑
BpBinder *proxy = binder->remoteBinder();
if (proxy == NULL) {
ALOGE("null proxy");
}
const int32_t handle = proxy ? proxy->handle() : ;
obj.type = BINDER_TYPE_HANDLE;
obj.binder = ; /* Don't pass uninitialized stack data to a remote process */
obj.handle = handle;
obj.cookie = ;
} else { // local类型的binder封装逻辑
obj.type = BINDER_TYPE_BINDER;
obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
obj.cookie = reinterpret_cast<uintptr_t>
(local);
}

} else {
obj.type = BINDER_TYPE_BINDER;
obj.binder = ;
obj.cookie = ;
} return finish_flatten_binder(binder, obj, out);
}

它根据传入binder的类型做不同的数据封装,在frameworks/native/include/binder/IBinder.h:139,可以看到IBinder声明了两个虚函数:

class IBinder : public virtual RefBase
{
public:
……
virtual BBinder* localBinder();
virtual BpBinder* remoteBinder();
……
};

并在frameworks/native/libs/binder/Binder.cpp:47定义了默认实现:

BBinder* IBinder::localBinder()
{
return NULL;
} BpBinder* IBinder::remoteBinder()
{
return NULL;
}

flat_binder_object这个数据结构在《Binder学习笔记(四)—— ServiceManager如何响应checkService请求》研究ServiceManager如何组织reply数据时遇到过,它定义在external/kernel-headers/original/uapi/linux/binder.h:57。对于不同的binder封装成的数据示意图如下:

  

然后flatten_binder(…)调用finish_flatten_binder(…),frameworks/native/libs/binder/Parcel.cpp:199

inline static status_t finish_flatten_binder(
const sp<IBinder>& /*binder*/, const flat_binder_object& flat, Parcel* out)
{
return out->writeObject(flat, false);
}

继续调用writeObject(…),frameworks/native/libs/binder/Parcel.cpp:1035

status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData)
{
const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity;
const bool enoughObjects = mObjectsSize < mObjectsCapacity;
if (enoughData && enoughObjects) {
restart_write:
// 如果空间足够,他把前面组装的flat_binder_object实体追加到mData里
*reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val;
……
if (nullMetaData || val.binder != ) {
// mObjects记录每次向mData追加的flat_binder_object的偏移位置
mObjects[mObjectsSize] = mDataPos;
acquire_object(ProcessState::self(), val, this, &mOpenAshmemSize);
mObjectsSize++;
} return finishWrite(sizeof(flat_binder_object));
}
……
}

总结一下:Parcel的数据区域分两个部分:mData和mObjects,所有的数据不管是基础数据类型还是对象实体,全都追加到mData里,mObjects是一个偏移量数组,记录所有存放在mData中的flat_binder_object实体的偏移量。Parcel的数据模型如下:

Binder学习笔记(五)—— Parcel是怎么打包数据的?的更多相关文章

  1. Binder学习笔记(九)—— 服务端如何响应Test()请求 ?

    从服务端代码出发,TestServer.cpp int main() { sp < ProcessState > proc(ProcessState::self()); sp < I ...

  2. Binder学习笔记(十二)—— binder_transaction(...)都干了什么?

    binder_open(...)都干了什么? 在回答binder_transaction(...)之前,还有一些基础设施要去探究,比如binder_open(...),binder_mmap(...) ...

  3. C#可扩展编程之MEF学习笔记(五):MEF高级进阶

    好久没有写博客了,今天抽空继续写MEF系列的文章.有园友提出这种系列的文章要做个目录,看起来方便,所以就抽空做了一个,放到每篇文章的最后. 前面四篇讲了MEF的基础知识,学完了前四篇,MEF中比较常用 ...

  4. (转)Qt Model/View 学习笔记 (五)——View 类

    Qt Model/View 学习笔记 (五) View 类 概念 在model/view架构中,view从model中获得数据项然后显示给用户.数据显示的方式不必与model提供的表示方式相同,可以与 ...

  5. java之jvm学习笔记五(实践写自己的类装载器)

    java之jvm学习笔记五(实践写自己的类装载器) 课程源码:http://download.csdn.net/detail/yfqnihao/4866501 前面第三和第四节我们一直在强调一句话,类 ...

  6. Learning ROS for Robotics Programming Second Edition学习笔记(五) indigo computer vision

    中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS for Robotics Pr ...

  7. Typescript 学习笔记五:类

    中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...

  8. golang学习笔记8 beego参数配置 打包linux命令

    golang学习笔记8 beego参数配置 打包linux命令 参数配置 - beego: 简约 & 强大并存的 Go 应用框架https://beego.me/docs/mvc/contro ...

  9. ES6学习笔记<五> Module的操作——import、export、as

    import export 这两个家伙对应的就是es6自己的 module功能. 我们之前写的Javascript一直都没有模块化的体系,无法将一个庞大的js工程拆分成一个个功能相对独立但相互依赖的小 ...

随机推荐

  1. 蓝桥杯 基础练习 BASIC-25 回形取数

    基础练习 回形取数   时间限制:1.0s   内存限制:512.0MB 问题描述 回形取数就是沿矩阵的边取数,若当前方向上无数可取或已经取过,则左转90度.一开始位于矩阵左上角,方向向下. 输入格式 ...

  2. 机器学习:集成学习(Ada Boosting 和 Gradient Boosting)

    一.集成学习的思路 共 3 种思路: Bagging:独立的集成多个模型,每个模型有一定的差异,最终综合有差异的模型的结果,获得学习的最终的结果: Boosting(增强集成学习):集成多个模型,每个 ...

  3. Java-API:java.util.regex.Pattern

    ylbtech-Java-API:java.util.regex.Pattern 1.返回顶部   2.返回顶部   3.返回顶部   4.返回顶部   5.返回顶部 1. https://docs. ...

  4. shell脚本 回顾 小练习

    1.把/OPT目录下(包含子目录)下所有后缀为“.sh”的文件后缀变更为“.shell” 2.将A.B.C目录下的文件A1.A2.A3文件改名为A4.A5.A63.如何在vi模式下将文件中的aa字符串 ...

  5. C# EntityFramwork(Model First)使用要点

    本文介绍EntityFramework使用方法 Entity Framework的注意点 由于安装和操作的细节讲起来很琐碎,这部分只罗列出难点,其他细节请自行查阅 安装细节 Pluralize or ...

  6. Eclipse中Next Difference的快捷键

    原文:http://stackoverflow.com/questions/10177460/is-there-a-key-binding-for-next-difference-and-previo ...

  7. DAY11-MYSQL视图、触发器、事务、存储过程、函数

    一 视图 视图是一个虚拟表(非真实存在),其本质是[根据SQL语句获取动态的数据集,并为其命名],用户使用时只需使用[名称]即可获取结果集,可以将该结果集当做表来使用. 使用视图我们可以把查询过程中的 ...

  8. ListView显示Sqlite的数据

    在安卓中,ListView和Sqlite都是十分常用的.这次我们来结合这个两个知识点写一个Demo. 功能:吧SQLite中的数据用ListView显示出来. 先看截图吧 首先是数据库 然后是运行截图 ...

  9. JSP 按钮点击,onclick事件中的AJAX不执行可能的原因

    <button onclick="deleteAccount()"  >删除</button> 缺少了 type="button" &l ...

  10. 第四天:servlet的生命周期和一些细节问题

    1.  servlet的生命周期: a)  流程 i.  Web服务器首先会检查是否装载了该servlet的实例对象.如果装载了直接进行第四步. ii.  装载并创建该servlet的实例对象. ii ...