• 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.
    ");
                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设计与实现 – 设计篇

  • 相关阅读:
    JavaScript递归函数解“汉诺塔”
    《JavaScript DOM编程艺术》学习笔记(三)
    《JavaScript DOM编程艺术》学习笔记(二)
    《JavaScript DOM编程艺术》学习笔记(一)
    《手把手教你实现电商网站开发》课程学习总结
    学校水卡、本地公交卡破解记
    第一篇博客
    mysql5.7.5以上版本使用distinct和order by 报错备忘录
    group_concat长度限制踩坑备忘录
    ArrayList、LinkedList、Vector的区别
  • 原文地址:https://www.cnblogs.com/zhanghaiyublog/p/3620855.html
Copyright © 2020-2023  润新知