2.4 C++中MediaPlayer的C/S架构
在前面几节中,都是通过Java层调用到JNI层中,而JNI层向下到C++层并未介绍。本节首先分析Java层的一个函数在C++层MediaPlayer中的过程(路径为frameworks/av/media/libmedia/MediaPlayer.cpp)。
下面找一个我们熟悉的setDataSource函数来看看C(Client)/S(Server)模式的过程。setDataSource函数如下:
对应看看MediaPlayerService.cpp中的create函数,MediaPlayerService.cpp在C++ 6.0源码中处于frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp中,代码如下:
在new Client中,有一个IPCThreadState。在Android中ProcessState是客户端和服务器端公共的部分,作为Binder通信的基础。ProcessState是一个singleton类,每个进程只有一个对象,这个对象负责打开Binder驱动,建立线程池,让其进程里面的所有线程都能通过Binder通信。
与之相关的是IPCThreadState,每个线程都有一个IPCThreadState实例登记在Linux线程的上下文附属数据中,主要负责Binder的读取、写入和请求处理。IPCThreadState在构造的时候获取进程的ProcessState并记录在自己的成员变量mProcess中,通过mProcess可以获得Binder的句柄。IPCThreadState通过IPCThreadState::transact把data及handle等填充进binder_transaction_data,在两个进程间通信。
这里这个Client到底是什么?我们又得追踪一下,在frameworks/av/media/libmediaplayerservice/MediaPlayerService.h中,如下:
以上代码对应Java层的MediaPlayer相关方法。如果还记得图2-3的话,可以从整体上理解这个Client属于什么角色及位置。继承BnMediaPlayer,并包含了IMediaPlayer相关接口。
总结一下上面的代码,Client类的继承关系为Client->BnMediaPlayer->IMediaPlayer。分析上面的代码可以看出,create函数构造了一个Client对象,并将此Client对象添加到MediaPlayerService类的全局列表mClients中,这是一个SortedVector,紧接着执行player->setDataSource(url,headers),即Clients::setDataSource,因此在setDataSource中有如下语句:
等价于
即player最终是用Client对象来初始化的,可以直接认为player==Client。
这时候问题来了,在C++中,这个Client及MediaPlayer又是什么关系呢?
• Client是MediaPlayerService内部的一个类,我们从上面的代码已知,因为MediaPlayerService运行在服务器端,故Client也运行在服务器端。
• Client在MediaPlayerService.h中,那接着看看MediaPlayerService中的实现,实现过程中调用过MediaPlayerService类的一些函数,同样回到setDataSource。代码如下:
接下来重新看看MediaPlayer中头文件定义的函数声明,方便对比Client中的函数,以下代码在frameworks/av/include/media/mediaplayer.h中:
这里的函数和Client中的函数是一一对应的,两者通过Client的代理类联系在了一起:
上面的两个函数,一个是MediaPlayer的setDataSource,会调到attachNewPlayer函数,这个函数最终会调用服务器端Client对应的函数。到这里可能有读者会想,IMediaPlayer.h和mediaplayer.h的区别是什么?那么下面介绍一下IMediaPlayer.h、mediaplayer.h、ImediaPlayer-Client.h的区别。
• 从包结构上看:IMediaPlayer和IMediaPlayerClient.h都在frameworks/av/media/libmedia包中,而mediaplayer.h在/av/include/media包中(前面已有代码贴出)。
• 从功能上看:它们肩负的职责也不一样。
这里贴出IMediaPlayer.h及IMediaPlayerClient.h的代码,IMediaPlayer.h位于frameworks/av/media/libmedia包中:
在IMediaPlayer.h中定义的基本上都是虚函数,而我们知道虚函数在C++中用于实现多态性(Polymorphism),多态性是将接口与具体实现代码进行了分离,用形象的语言来解释就是以共同的方法实现,但因个体差异而采用不同的策略。所以它的功能是实现MediaPlayer功能的接口,看到onTransact函数,自然联想Binder通信,把底层的Parcel指针类型数据向上层的另一个进程传递。
再分析一下IMediaPlayerClient.h,同样位于frameworks/av/media/libmedia包中:
总结一下上面的代码,在内部定义一个BpMediaPlayerClient类(也就是Client的父类),然后它有一个onTransact函数。一般onXXX都是被动回调过来的,不是由自己控制的,如Activity中的onCreate、onPause、onStart函数,这些函数都是在其他地方处理并通知到Activity中的。这里也是一样的,onTransact作为Binder通信中的回调函数,前面介绍到player实际上是C/S模式的,IMediaPlayerClient.h的功能是描述一个MediaPlayer客户端的接口。
综上所述,mediaplayer.h的功能是对外(JNI层)的接口类,它最主要的是定义了一个MediaPlayer类(C++层),我们在android_media_MediaPlayer.cpp中就引入了media/mediaplayer.h;IMediaPlayer.h则是一个实现MediaPlayer(C++层)功能的接口;而IMediaPlayerClient.h的功能是描述一个MediaPlayer客户端(这里暂且理解为前面说的Client)的接口。