android的binder机制研究C++部分.docx
- 文档编号:29860051
- 上传时间:2023-07-27
- 格式:DOCX
- 页数:17
- 大小:123.74KB
android的binder机制研究C++部分.docx
《android的binder机制研究C++部分.docx》由会员分享,可在线阅读,更多相关《android的binder机制研究C++部分.docx(17页珍藏版)》请在冰豆网上搜索。
android的binder机制研究C++部分
(一)概述
android的binder机制提供一种进程间通信的方法,使一个进程可以以类似远程过程调用的形式调用另一个进程所提供的功能。
binder机制在Java环境和C/C++环境都有提供。
android的代码中,与C/C++的binder包括一些类型和接口的定义和实现,相关的代码在下面这几个文件中:
frameworks\base\include\utils\IInterface.h
frameworks\base\include\utils\Binder.h
frameworks\base\include\utils\BpBinder.h
frameworks\base\include\utils\IBinder
frameworks\base\include\utils\Parcel.h
frameworks\base\include\utils\IPCThreadState.h
frameworks\base\include\utils\ProcessState.h
frameworks\base\libs\utils\Binder.cpp
frameworks\base\libs\utils\BpBinder.cpp
frameworks\base\libs\utils\IInterface.cpp
frameworks\base\libs\utils\IPCThreadState.cpp
frameworks\base\libs\utils\Parcel.cpp
frameworks\base\libs\utils\ProcessState.cpp
为了了解这些类、接口之间的关系以及binder的实现机制,最好是结合一个例子来进行研究。
我选择的例子是android自带的媒体播放器的实现。
其媒体播放器的相关代码在下面这些目录中:
frameworks\base\include\media
frameworks\base\media
使用startUML的反向工程功能分析上面这些代码,并进行了一定的整理之后,得到下面这幅类图(点击可查看原尺寸图片)。
android的媒体播放功能分成两部分,一部分是媒体播放应用,一部分是媒体播放服务(MediaServer,在系统启动时由init所启动,具可参考init.rc文件)。
这两部分分别跑在不同的进程中。
媒体播放应用包括Java程序和部分C++代码,媒体播放服务是C++代码,并且需要调用外部模块opencore来实现真正的媒体播放。
媒体播放应用和媒体播放服务之间需要通过binder机制来进行相互调用,这些调用包括:
(1)媒体播放应用向媒体播放服务发控制指令
(2)媒体播放服务向媒体播放应用发事件通知(notify)
媒体播放服务对外提供多个接口,在上面得图中包括其中的2个接口:
IMediaService和IMediaPlayer,IMediaplayer用于创建和管理播放实例,而IMediaplayer接口则是播放接口,用于实现指定媒体文件的播放以及播放过程的控制。
上面的图中还有媒体播放应用向媒体播放服务提供的1个接口:
IMediaPlayerClient,用于接收notify。
这些接口因为需要跨进程调用,因此需要用到binder机制。
每个接口包括两部分实现,一部分是接口功能的真正实现(BnInterface),这部分运行在接口提供进程中;另一部分是接口的proxy(BpInterface),这部分运行在调用接口的进程中。
binder的作用就是让这两部分之间建立联系。
下图是整个播放器的一个概要说明。
媒体播放器比较复杂一些,总共实现了3个接口,不过要了解binder的机制,只需要研究其中一个接口就足够了。
在这里选择IMediaPlayerService接口来看一下。
IMediaPlayerService接口包括六个功能函数:
create(url)、create(fd)、decode(url)、decode(fd)、createMediaRecord()、createMetadataRetriever()。
在这里不介绍这些函数是做什么的,我们只关注如何通过binder还提供这些函数接口。
(二)接口定义
(1)定义接口类
首先定义IMediaPlayerService类,这是一个接口类(C++的术语应该叫纯虚类)。
该接口类定义在文件frameworks\base\include\media\IMediaPlayerService.h。
代码如下:
classIMediaPlayerService:
publicIInterface
{
public:
DECLARE_META_INTERFACE(MediaPlayerService);
virtualsp
virtualsp
virtualsp
virtualsp
virtualsp
virtualsp
};
可以看到,在这个接口类中定义了IMediaPlayerService需要提供的6个函数接口,因为是接口类,所以定义为纯虚函数。
需要注意这个接口类的名称有严格要求,必须是以大写字母I开始。
重点关注在这些函数前面的一个宏定义:
DECLARE_META_INTERFACE(MediaPlayerService)。
这个宏定义必须要有,其中封装了实现binder所需要的一些类成员变量和成员函数通过这些成员函数可以为一个binder实现创建proxy。
这个宏定义在问价frameworks\base\include\utils\IInterface.h里,在后面还会讲到。
这个宏定义的参数必须是接口类的名称去除字母I后剩下的部分。
另外说明一下,可以看到接口类中所定义的函数的返回值都是sp
sp是android中定义的一个模板类,用于实现智能指针功能。
sp
(2)定义和实现binder类
binder类包括两个,一个是接口实现类,一个接口代理类。
接口代理类继承自BpInterface,接口实现类继承自BnInterface。
这两个基类都是模板类,封装了binder的进程间通信机制,这样使用者无需关注底层通信实现细节。
对于IMediaPlayerService接口,其binder接口实现类为BnMediaPlayerService,接口代理类为BpMediaPlayerService。
需注意这两个类的名称有严格要求,必须以Bn和Bp开头,并且后面的部分必须是前面所定义的接口类的名称去除字母'I’。
比如前面所定义的接口类为IMediaPlayerService,去除字母I后是MediaPlayerService,所以两个binder类的名称分别是BnMediaPlayerService和BpMediaPlayerService。
为什么有这样的要求?
原因就在前面提到的宏定义DECLARE_META_INTERFACE()和另一个宏定义IMPLEMENT_META_INTERFACE()里面。
有兴趣的话可以去看一下,这两个宏定义都在文件frameworks\base\include\utils\IInterface.h里。
BpMediaPlayerService是一个最终实现类。
定义并且实现在在文件frameworks\base\media\libmidia\IMediaPlayerService.cpp中。
在看BpMediaPlayerService的代码之前,先看一下在IMediaPlayerService.cpp文件的开始部分的一个枚举定义:
enum{
CREATE_URL=IBinder:
:
FIRST_CALL_TRANSACTION,
CREATE_FD,
DECODE_URL,
DECODE_FD,
CREATE_MEDIA_RECORDER,
CREATE_METADATA_RETRIEVER,
};
这些6个枚举定义对应于IMediaPlayerService接口所提供的6个功能函数,可以称为这些功能函数的功能代码,用于在进程之间进行RPC是标识需要调用哪个函数。
如果不想定义这些枚举值,在后面需要用到这些值的地方直接写上1,2,3,4,5,6也是可以的,不过……一个合适的程序员会这么干吗?
下面看一下BpMediaPlayerService的代码。
(3)BpMediaPlayerService代码分析
classBpMediaPlayerService:
publicBpInterface
{
public:
BpMediaPlayerService(constsp
:
BpInterface
{
}
virtualsp
{
Parceldata,reply;
data.writeInterfaceToken(IMediaPlayerService:
:
getInterfaceDescriptor());
data.writeInt32(pid);
remote()->transact(CREATE_METADATA_RETRIEVER,data,&reply);
returninterface_cast
}
virtualsp
{
Parceldata,reply;
data.writeInterfaceToken(IMediaPlayerService:
:
getInterfaceDescriptor());
data.writeInt32(pid);
data.writeStrongBinder(client->asBinder());
data.writeCString(url);
remote()->transact(CREATE_URL,data,&reply);
returninterface_cast
}
virtualsp
{
Parceldata,reply;
data.writeInterfaceToken(IMediaPlayerService:
:
getInterfaceDescriptor());
data.writeInt32(pid);
remote()->transact(CREATE_MEDIA_RECORDER,data,&reply);
returninterface_cast
}
virtualsp
{
Parceldata,reply;
data.writeInterfaceToken(IMediaPlayerService:
:
getInterfaceDescriptor());
data.writeInt32(pid);
data.writeStrongBinder(client->asBinder());
data.writeFileDescriptor(fd);
data.writeInt64(offset);
data.writeInt64(length);
remote()->transact(CREATE_FD,data,&reply);
returninterface_cast
}
virtualsp
{
Parceldata,reply;
data.writeInterfaceToken(IMediaPlayerService:
:
getInterfaceDescriptor());
data.writeCString(url);
remote()->transact(DECODE_URL,data,&reply);
*pSampleRate=uint32_t(reply.readInt32());
*pNumChannels=reply.readInt32();
*pFormat=reply.readInt32();
returninterface_cast
}
virtualsp
{
Parceldata,reply;
data.writeInterfaceToken(IMediaPlayerService:
:
getInterfaceDescriptor());
data.writeFileDescriptor(fd);
data.writeInt64(offset);
data.writeInt64(length);
remote()->transact(DECODE_FD,data,&reply);
*pSampleRate=uint32_t(reply.readInt32());
*pNumChannels=reply.readInt32();
*pFormat=reply.readInt32();
returninterface_cast
}
};
首先可以看到,这个类继承自模板类BpInterface,指定类型为接口类IMediaPlayerService。
BpInterface模板类定义在文件IInterface.h。
看一下BpInterface的定义就可以发现,BpMediaPlayerService这样定义了以后,事实上间接继承了IMediaPlayerService,从而可以提供IMediaPlayerService接口所定义的接口函数。
BpMediaPlayerService需要实现这些接口函数。
在一个简单的构造函数之后,就是这些接口函数的实现。
可以看到,所有的接口函数的实现方法都是一致的,都是通过binder所提供的机制将参数仍给binder的实现类,并获取返回值。
这也就是这个类之所以成为代理类的原因。
下面具体看一下一个接口函数。
这里选的是函数create(url)。
virtualsp
{
Parceldata,reply;
data.writeInterfaceToken(IMediaPlayerService:
:
getInterfaceDescriptor());
data.writeInt32(pid);
data.writeStrongBinder(client->asBinder());
data.writeCString(url);
remote()->transact(CREATE_URL,data,&reply);
returninterface_cast
}
这个接口函数的参数指定了一个URL,函数将为这个URL创建一个播放器实例用于播放该URL。
函数首先定义了两个局部变量data和reply,变量的类型都是Parcel。
Parcel是一个专为binder通信的数据传送而定义的类,该类提供了对多种类型的数据的封装功能,同时提供多个数据读取和写入函数,用于多种类型的数据的写入和读取,支持的数据类型既包括简单数据类型,也包括对象。
这里定义的变量data是用于封装create()函数调用所需要的输入参数,而reply则是用于封装调用的返回数据(包括输出参数的值和函数返回值)。
函数首先向data中写入各种数据。
第一个写入的是接口的一个描述字符串,binder的实现类中会用这个字符串来对接口做验证,防止调用错误。
这个字符串也可以不写,如果不写,在binder实现类中相应的也就不要做验证了。
跟在描述字符串后面写入的是该接口函数所需要的各种的输入参数。
需要说明的是,Pacel提供一种先入先出的数据存储方式,即数据的写入顺序和读取顺序必须严格一致,否则将会出错。
完成数据写入后,函数调用remote()->transact()用于完成binder通信。
transact()函数的第一个参数就是前面提到过的功能代码。
transact()的功能是将data中的数据传给binder的实现类,函数调用结束后,reply中将包含返回数据。
首先来看看remote()成员函数。
前面讲到过BpMediaPlayerService通过继承BpInterface模板类间接继承了IMediaPlayerService接口类,其实BpInterface类是一个有两个父类的多重继承子类,另一个父类是BpRefbase(frameworks\base\include\utils\Binder.h)。
remote()就是继承自BpRefBase类的一个成员函数,该函数返回BpRefBase类中定义的一个私有属性mRemote。
mRemote是对IBinder接口类的子类BpBinder的一个对象的引用(参考前面的类关系图)。
transact()函数在IBinder接口类中定义(frameworks\base\include\utils\Binder.h),并在BpBinder类中实现(frameworks\base\include\utils\BpBinder.h、frameworks\base\libs\utils\BpBinder.cpp)。
在transact()函数中将调用IPCThreadState类的transact()函数,并进而通过Lniux内核中的android共享内存驱动来实现进程间通信。
不过这些细节这里就不多说了。
在这里BpBinder类对象是一个关键,是实现Binder代理的核心之一。
BpBinder类可以看成是一个通信handle(类似于网络编程中的socket),用于实现进程间通信。
接下来需要研究的是这个BpBinder类对象(即mRemote成员变量的值)是从哪里来的。
回过头来BpMediaPlayerService的构造函数(看前面的代码)。
该构造函数的参数是一个IBinder对象的引用。
mRemote的值就是在这里传进来的这个对象。
那么这个对象又是怎么来的呢?
要搞清楚这一点就需要找到创建BpMediaPlayerService类的实例的代码,这个代码就就跟在该类的定义代码的下面。
继续看IMediaPlayerService.cpp文件,在BpMediaPlayerService类定义的后面,是下面这样一行代码:
IMPLEMENT_META_INTERFACE(MediaPlayerService,
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- android binder 机制 研究 C+ 部分