Android之进程通信--Binder

  1. 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.\n");

            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驱动内部的结构如图所示:image

参考阅读:Android深入浅出之Binder机制

Android进程间通信(IPC)机制Binder简要介绍和学习计划

Android Binder设计与实现 – 设计篇

你可能感兴趣的:(android)