(一) 概述
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。代码如下:
class IMediaPlayerService: public IInterface { public:
DECLARE_META_INTERFACE(MediaPlayerService);
virtual sp
virtual sp
virtual sp
pNumChannels, int* pFormat) = 0; };
virtual sp
virtual sp
virtual sp
可以看到,在这个接口类中定义了IMediaPlayerService需要提供的6个函数接口,因为是接口类,所以定义为纯虚函数。需要注意这个接口类的名称有严格要求,必须是以大写字母I开始。
重点关注在这些函数前面的一个宏定义:
DECLARE_META_INTERFACE(MediaPlayerService)。这个宏定义必须要有,其中封装了实现binder所需要的一些类成员变量和成员函数通过这些成员函数可以为一个binder实现创建proxy。这个宏定义在问价frameworks\\base\\include\%utils\\IInterface.h里,在后面还会讲到。这个宏定义的参数必须是接口类的名称去除字母I后剩下的部分。 另外说明一下,可以看到接口类中所定义的函数的返回值都是sp
sp
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代码分析