引言
在上一篇Blog中,在分析服务注册过程时,往data(Parcel对象)变量写入数据时,有这样的调用路径:
BpServiceManager::addService()–>Parcel::writeStrongBinder()–>flatten_binder()–>finish_flatten_binder()
由于finish_flatten_binder()方法中涉及到的东西太多,在上一篇博客就没有展开来讲。这篇博客将详细分析数据是如何写入到data中的。
下面是Parcel类的定义:
虽然public方法有很多,但是我们先只要注意它的成员变量即可,特别是mData,mDataSize,mDataCapacity,mDataPos,mObjects,mObjectsSize,mObjectsCapacity这几个成员变量。
另外,为了方便大家理解,先说1个结论:对于普通数据,使用mData进行储存;对于IBinder类型的数据以及FileDescriptor使用的是mObjects;
1.Parcel类的初始化
在C++中,类的初始化非常重要,可能在成员初始化列表中就进行非常多的操作。所以首先要看它的构造函数:
出乎意料地是,Parcel类的构造方法异常简单,就是调用initState()方法,下面是initState()的代码:
可以发现,initState()函数主要就是对成员变量初始化。而mDataSize,mDataCapacity,mDataPos,mObjectsSize,mObjectsCapacity都为0,另外指针都初始化为NULL.注意这些成员的初始值很重要,因为后面会用到。
2.finish_flatten_binder()
由于在上一篇Blog中已经分析过Parcel::writeStrongBinder()和flatten_binder()函数,所以这里直接分析finish_flatten_binder()方法了。该方法的代码如下:
竟然又是调用,这个嵌套太多了,而且binder参数没有再被用到的话,其实就可以不传递过来了。下面看Parcel::writeObject()方法的代码:
由于从Parcel data;定义之后,一直到这里,除了写入接口描述符和服务名称之外,并没有改变data的其他成员变量,所以mDataPos,mDataCapacity,mObjectsSize,mObjectsCapacity仍然是初始值0,所以enoughData,enoughObjects均为false.
所以先执行code_5和code_6处的代码,先看growData()方法的代码:
其中len是flat_binder_object的大小,显然这里无论如何都不会出现newSize<=mDataSize的情况,所以这里是个很明显的bug,存在着内存泄露的风险。
下面我们看一下continueWrite(newSize)的代码,注意此时传入的参数值是(mDataSize+len)*3/2:
其实这个方法写得挺糟糕的,有那么多分支,应该增加几个工具函数,这样一个函数的代码量就不会那么大了,阅读代码就会方便很多。
由于传入的desired值为(mDataSize+size(val))*3/2,肯定大于mDataSize,再加上mOwner和mData仍然为NULL,所以这里不会执行code_1,code_2和code_3这3部分代码。只需要看code_4这个分支下的代码。
这段代码非常简单,就是先调用malloc()方法分配一块(mDataSize+size(val))*3/2大小的内存,然后让mData指向该内存,并且将mDataCapacity的值修改为分配内存的大小。
到这里可以归纳一下,growData()方法只是分配了一块比flat_binder_object对象大一些的内存,但是暂时还没有将数据放进去。
=============================================================
再回到Parcela::writeObject()方法中,下面执行的仍然是从堆上分配内存,分配好之后让mObjects指向内存的起始位置,并且mObjectsCapacity赋值为刚刚分配的内存大小。另外,由于mObjectsSize初始值为0,所以这里其实newSize==3,从而分配的内存大小其实是3*sizeof(size_t);其中size_t是typedef unsigned long size_t,所以sizeof(size_t)的结果为4,从而这里最终是分配了12个字节的内存大小。
接着是goto restart_write,再次吐槽一句,Android Framework的架构虽然很好,但是有些代码质量堪忧,像这样的goto语句在很多个地方出现过。还有像interpret_cast这样很容易导致程序crash的转化其实也应该尽量避免的。
reinterpret_cast<flat_binder_object>(mData+mDataPos)=val;的作用是将从mData+mDataPos位置处开始的内存内容填充为flat_binder_object对象val的值;
虽然nullMetaData为false,但是val.binder不为NULL,所以下面会给mObjects数组赋值,此时mObjectsSize==0,所以mObjects数组中第一个元素为mDataPos,下面进入acquire_object()方法:
注意到传入的参数分别是:ProcessState::self(),flat_binder_object对象以及Parcel对象,由于obj.type为BINDER_TYPE_BINDER,所以obj的cookie对象,其实就是之前创建的MediaPlayerService对象执行incStrong()函数,这里涉及到指针的引用问题,后面会有专门的博客进行深入讲解,这里可以将其简单地理解成为Parcel对象增加一次引用。
之后再回到Parcel::writeObject()方法中,mObjectsSize执行自加操作之后变为1.
==========================================================================================
由于val.type值是BINDER_TYPE_BINDER,所以下面进入到Parcel::finishWrite()方法中,注意传入的参数sizeof(flat_binder_object)值为16:
这个方法异常简单,就是让mDataPos增加到刚刚写入数据的末尾,并且进行一个判断,如果mDataPos>mDataSize的话,就将mDataSize=mDataPos;而实际上mDataSize在赋值前还是0,所以会进行这个赋值操作,因此我们可以知道,其实mDataSize是记录当前mData中写入数据的大小。
到这里,我们就将flat_binder_object这个对象写入到Parcel对象中的mData指向的内存中。