- Cilent从ServiceManger哪里获得BnMediaService的BnBinder引用就可以调用BnMediaPlayerService的方法了,BnMediaPlayerService是怎样响应客户端的请求的哪?
BnMediaService并不是直接接受Cilent发送过来的请求,而是使用IPCThreadState接受Cilent发送过来的请求,然后调用BBinder的transact函数,传入客户端发送过来的相关参数,由transact函数来调用BnMediaPlayerService类的onTransact函数来真正的处理Cilent请求的。也就是说Cilent的请求是经过了IPCThreadState的中转才调用BnMediaService的onTransact服务的。为什么要中转哪?因为这是跨进程的对象访问,并不是在进程内的直接访问,要让跨进程的对象访问像自己内部对象访问一样,必须经过底层驱动的一个转换过程,也就是说在IPCTheadState线程里完成了和底层Binder的通信。
3.通常客户端如何和服务器端通信的流程?
LayoutInflater layoutInflater=(LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//1.通常情况下是客户端要和服务器端通信就是调用ServieManager远程接口提供的getService接口来实现的。这样就获得了服务器端的跨进程引用
View view =layoutInflater.inflate(R.layout.activity_second, null);
//2.客户端直接调用服务引用的方法
//3.服务器端的IPCTHreadState线程接受到客户端的请求后会调用BBinder的transat函数,传入客户端的请求,然后由transact函数自动调用服务器端的onTransact函数(为什么这里调用IPCThreadState而不是直接在Service里调用,因为Service端是多线程的,也就是服务器端可以有几个至几十个客户端,而用线程正好可以给每个客户端请求开启一个IPCTheadState线程)
//4.服务器端在onTransate函数里执行对外开发的服务方法,返回执行结果。
4.通常的C/S的通信流程是从客户端有ServiceManger的远程接口开始的,正式因为客户端一开始就有ServiceManger的远程接口,所以才能调用getService 接口,才有后面的流程,那么客户端如何获得ServiceManger的远程接口的哪?
//1.客户端直接调用defaultServiceManager就可以获得ServiceManger的引用 ServiceManager
5.ServiceManager有很多父类和实现了很多接口,最终ServiceManger有一个很重要的成员变量mRemote他的类型是IBinder*,实现类就是BpBinder。ServiceManger服务的对外监听客户端请求的线程IPCThreadState有一个成员变量mProcess,这个mProcess的类型就是ProcessState。一个进程里只有一个PoocessState,当客户端调用defaultServiceManger的时候就是在请求BpBinder。
/**** 过程如下: 1.客户端(可能是MediaServiceManager也可能是一个自定义普通的Service,无论这个对象是Service Cilent 还是ServiceManger在这个时候相对于ServiceManger这个Linux init函数最先加载的进程都是客户端)调用defaultServiceManger,那么ServiceMnager就会启动一个IPCThreadState线程,然后会同过ProcessState打开/dev/binder设备 2.返回一个句柄值为0的binder引用 ****/
6.Android和Binder驱动通信都是在ProcessState里完成的,因为只有ProcessState打开了/dev/binder文件,分析ProcessState的构造函数
ProcessState::ProcessState()
: mDriverFD(open_driver())//打开/dev/binder文件,并把设备文件描述符保存在mDriverFD成员变量中
//打开文件设备的时候会使用BINDER_VERSION和BINDER_SET_MAX_THREADS命令和Binder驱动程序交互,前者用来获得驱动程序的版本号,后者告诉驱动Server端最多可以打开的线程的数量。和驱动交互本身就是和内核态的程序交互,发送命令获得返回值。
, mVMStart(MAP_FAILED) , mManagesContexts(false) , mBinderContextCheckFunc(NULL) , mBinderContextUserData(NULL) , mThreadPoolStarted(false) , mThreadPoolSeq(1) { if (mDriverFD >= 0) { // XXX Ideally, there should be a specific define for whether we // have mmap (or whether we could possibly have the kernel module // availabla). #if !defined(HAVE_WIN32_IPC) // mmap the binder, providing a chunk of virtual address space to receive transactions. mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0); if (mVMStart == MAP_FAILED) { // *sigh* LOGE("Using /dev/binder failed: unable to mmap transaction memory. "); close(mDriverFD); mDriverFD = -1; } #else mDriverFD = -1; #endif } if (mDriverFD < 0) { // Need to run without the driver, starting our own thread pool. } }
7.Sever创建一个Binder实体,为其取一个字符形式可读易记的名字,将这个Binder连同名字以数据包的形式通过Binder驱动发送给SMGr,通知SMgr注册一个叫张三的Binder,它位于某个Server中,驱动为这个穿过进程边界的Binder创建位于内核中的实体节点以及SMgr对实体的引用,将名字及新建的引用传递给Smgr。SMgr接收到数据包后,从中取出名字和引用填写到一张查找表中。Server创建一个BInder实体后,就会通过AddService向ServiceManger注册,注册的时候因为是在进程间的通讯,所以必然要通过内核,内核就根据BInder实体的Parcel信息在内核中创建了一个实体,和引用,发送给ServerManger,这样当客户端向服务器发送请求的时候直接把数据发送到内核,内核经过ServerManger的唯一确定后,直接在内核中完成对Service方法的调用,因为无论是IPCThreadState还是ProcessState都是C++实现的在内核中完成的功能。java端只是实现了Binder会拥有哪些功能,而这个用java写成的功能会是被C++的代码接收请求和管理的。
package com.example.servicedemo3; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.util.Log; public class ServiceDemo extends Service { @Override public int onStartCommand(Intent intent, int flags, int startId) { // TODO Auto-generated method stub Log.d("Start COmmand","Startcommad 调用"); return super.onStartCommand(intent, flags, startId); } @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return new MyBlinder(); } class MyBlinder extends Binder{ public void checkWifi(){ System.out.println("----------------------------"); System.out.println("------------CheckWIfi----------------"); Log.d("CHeckWIFT","Service------------------"); } } }
8.当一个进程使用BINDER_SET_CONTEXT_MGR命令将自己注册成Servermanger的时候,BInder驱动就会自动的在驱动中创建一个ServerManger的实体(而一般的服务是在addServer的时候才会在驱动中创建实体),也就是说系统启动后,那些角色为ServerManager的进程当调用BINDER_SET_CONTEXT_MGR命令后,驱动就自动在BInder驱动里创建BpBinder,系统有多少ServiceManger,驱动就一一对应有多少BpBInder。他们的关系是平等。而当有Service向某个ServerManger注册BInder的时候其实注册的就是BBinder。驱动在自己的内存空间里维护着BpBinder和BBinder的一对多关系,这样当客户端需要调用某个服务的时候就会先取得BpBInder,当调用getService的时候就是由驱动在自己的内存中从BpBinder这个节点开始搜索对象名字的BBinder,如果查到就直接执行BBinder里的方法。因为客户端事实上是只有BpBinder的引用,所以就是这个过程中,首先把客户端的数据拷贝到内核中,当查询到指定的BBinder后,就可以直接执行了,而不必拷贝到别的地方。BInder驱动内部的结构如图所示:
参考阅读:Android深入浅出之Binder机制