Binder通信机制

Binder学习总结



一、Binder概述

binder是Android中进程间通信的机制。在Android framework层,binder是servicemanager连接各种manager和相应的managerservice的桥梁。对于Android应用层角度来说,binder是客户端和服务端进行通信的媒介。
在这里插入图片描述

二、Linux系统中的IPC原理

Android系统是基于linux内核的,了解linux ipc相关的概念有助于我们了解binder机制。

2.1基本知识介绍

2.1.1 进程隔离

在linux操作系统中,进程拥有自己的内存空间,各个进程的内存空间是互相隔离的,两个进程要进行数据交互必须采用进程间的通信机制即ipc。

2.1.1 进程空间划分以及系统调用

在Android系统中,采用的是虚拟内存管理技术,每个进程都有各自互不干涉的进程地址空间,该空间是虚拟空间。进程地址空间被分为两个部分,分别是用户空间和内核空间,用户进程通常情况下只能访问到用户空间的虚拟地址,只有用户进程进行系统调用等时刻可以访问到内核空间。每当进程进行切换时,用户空间会跟着变换,而内核空间不会随着进程改变。
系统调用时用户进程访问内核空间的方式,其保证了系统资源的访问都是在内核的控制下进行的,避免了用户程序对系统资源的越权访问,提升了系统的安全性和稳定性。进行执行用户空间的代码时处于用户态,当进行执行系统调用时称进程处于内核态
在这里插入图片描述

2.2 linux ipc原理

2.2.1 linux进程间通信的方式

pipe管道及FIFO有名管道

管道是linux支持的最初的ipc形式之一,具有以下特点:

  • 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道
  • 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)
  • 单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中
  • 数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。
  • 管道的缓冲区是有限的(管道制存在于内存中,在管道创建时,为缓冲区分配一个页面大小)
  • 管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式,比如多少字节算作一个消息(或命令、或记录)等等
    管道应用的一个重大限制是它没有名字,因此,只能用于具有亲缘关系的进程间通信,在有名管道(named pipe或FIFO)提出后,该限制得到了克服。FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中
信号

可参考文章:
http://www.ibm.com/developerworks/cn/linux/l-ipc/part2/index1.html
http://www.ibm.com/developerworks/cn/linux/l-ipc/part2/index2.html

消息队列

消息队列就是一个消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读走消息。消息队列与管道以及有名管道相比,具有更大的灵活性,首先,它提供有格式字节流,有利于减少开发人员的工作量;其次,消息具有类型,在实际应用中,可作为优先级使用。这两点是管道以及有名管道所不能比的。同样,消息队列可以在几个进程间复用,而不管这几个进程是否具有亲缘关系,这一点与有名管道很相似;但消息队列是随内核持续的,与有名管道(随进程持续)相比,生命力更强,应用空间更大。

共享内存

使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据[1]:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。
Linux的2.2.x内核支持多种共享内存方式,如mmap()系统调用,Posix共享内存,以及系统V共享内存。mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以向访问普通内存一样对文件进行访问,不必再调用read(),write()等操作

socket

更为一般的进程间通信机制,可用于不同机器之间的进程间通信
这些传统的 IPC 通信方式有两个问题:

  • 性能低下,一次数据传递需要经历:内存缓存区 --> 内核缓存区 --> 内存缓存区,需要 2 次数据拷贝;
  • 接收数据的缓存区由数据接收进程提供,但是接收进程并不知道需要多大的空间来存放将要传递过来的数据,因此只能开辟尽可能大的内存空间或者先调用 API 接收消息头来获取消息体的大小,这两种做法不是浪费空间就是浪费时间。

三、Binder 跨进程通信原理

3.1动态内核可加载模块

跨进程通信需要内核空间来实现,Linux的动态内核可加载模块的机制(Loadable Kernel Module,LKM),binder驱动模块是具有独立功能的程序,可以被单独编译,但是不能独立运行。其在运行的时候被链接到内核作为内核的一部分运行。Android系统通过动态添加一个binder模型运行在内核空间,用户进程之间通过这个binder内核模块作为桥梁实现通信。
binder机制是通过内存映射即mmap() 来实现,内存映射简单的讲就是将用户空间的一块内存区域映射到内核空间。映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间;反之内核空间对这段区域的修改也能直接反应到用户空间。内存映射能减少数据拷贝次数,实现用户空间和内核空间的高效互动。

3.1Binder IPC 实现原理

Binder IPC 正是基于内存映射(mmap)来实现的,但是 mmap() 通常是用在有物理介质的文件系统上的。Binder 驱动使用 mmap() 是用来在内核空间创建数据接收的缓存空间。
一次完整的 Binder IPC 通信过程通常是这样:

  • 首先 Binder 驱动在内核空间创建一个数据接收缓存区。
  • 接着在内核空间开辟一块内核缓存区,建立内核缓存区和内核中数据接收缓存区之间的映射关系,以及内核中缓存区和接收进程用户空间地址的映射关系;
  • 发送方进程通过系统调用 copy_from_user() 将数据 copy 到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信。
    在这里插入图片描述

四、Binder 通信模型

一次完整的进程间通信必然至少包含两个进程,通常我们称通信的双方分别为客户端进程(Client)和服务端进程(Server)

4.1.Client/Server/ServiceManager/驱动

Binder 是基于 C/S 架构的。由一系列的组件组成,包括 Client、Server、ServiceManager、Binder 驱动。其中 Client、Server、Service Manager 运行在用户空间,Binder 驱动运行在内核空间。其中 Service Manager 和 Binder 驱动由系统提供,而 Client、Server 由应用程序来实现。
在这里插入图片描述
Client、Server 和 ServiceManager 均是通过系统调用 open、mmap 和 ioctl 来访问设备文件 /dev/binder,从而实现与 Binder 驱动的交互来间接的实现跨进程通信

4.1.1Binder 驱动

Binder 驱动就如同路由器一样,是整个通信的核心;驱动负责进程之间 Binder 通信的建立,Binder 在进程之间的传递,Binder 引用计数管理,数据包在进程之间的传递和交互等一系列底层支持

4.1.2 ServiceManager 与实名 Binder

ServiceManager 和 DNS 类似,作用是将字符形式的 Binder 名字转化成 Client 中对该 Binder 的引用,使得 Client 能够通过 Binder 的名字获得对 Binder 实体的引用。注册了名字的 Binder 叫实名 Binder,就像网站一样除了除了有 IP 地址意外还有自己的网址。Server 创建了 Binder,并为它起一个字符形式,可读易记得名字,将这个 Binder 实体连同名字一起以数据包的形式通过 Binder 驱动发送给 ServiceManager ,通知 ServiceManager 注册一个名为“张三”的 Binder,它位于某个 Server 中。驱动为这个穿越进程边界的 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用,将名字以及新建的引用打包传给 ServiceManager。ServiceManger 收到数据后从中取出名字和引用填入查找表。ServierManager 是一个进程,Server 是另一个进程,Server 向 ServiceManager 中注册 Binder 必然涉及到进程间通信。当前实现进程间通信又要用到进程间通信

4.1.3 Client 获得实名 Binder 的引用

Server 向 ServiceManager 中注册了 Binder 以后, Client 就能通过名字获得 Binder 的引用了。Client 也利用保留的 0 号引用向 ServiceManager 请求访问某个 Binder: 我申请访问名字叫张三的 Binder 引用。ServiceManager 收到这个请求后从请求数据包中取出 Binder 名称,在查找表里找到对应的条目,取出对应的 Binder 引用作为回复发送给发起请求的 Client。从面向对象的角度看,Server 中的 Binder 实体现在有两个引用:一个位于 ServiceManager 中,一个位于发起请求的 Client 中。如果接下来有更多的 Client 请求该 Binder,系统中就会有更多的引用指向该 Binder ,就像 Java 中一个对象有多个引用一样。

4.2 Binder 通信过程

  • 首先,一个进程使用 BINDER_SET_CONTEXT_MGR 命令通过 Binder 驱动将自己注册成为 ServiceManager;
  • Server 通过驱动向 ServiceManager 中注册 Binder(Server 中的 Binder 实体),表明可以对外提供服务。驱动为这个 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用,将名字以及新建的引用打包传给 ServiceManager,ServiceManger 将其填入查找表。
  • Client 通过名字,在 Binder 驱动的帮助下从 ServiceManager 中获取到对 Binder 实体的引用,通过这个引用就能实现和 Server 进程的通信
    在这里插入图片描述

4.3 Binder 通信中的代理模式

当 A 进程想要获取 B 进程中的 object 时,驱动并不会真的把 object 返回给 A,而是返回了一个跟 object 看起来一模一样的代理对象 objectProxy,这个 objectProxy 具有和 object 一摸一样的方法,但是这些方法并没有 B 进程中 object 对象那些方法的能力,这些方法只需要把把请求参数交给驱动即可。对于 A 进程来说和直接调用 object 中的方法是一样的。当 Binder 驱动接收到 A 进程的消息后,发现这是个 objectProxy 就去查询自己维护的表单,一查发现这是 B 进程 object 的代理对象。于是就会去通知 B 进程调用 object 的方法,并要求 B 进程把返回结果发给自己。当驱动拿到 B 进程的返回结果后就会转发给 A 进程,一次通信就完成了

五、AIDL编码实现跨进程调用

5.1 各 Java 类职责描述

  • IBinder : IBinder 是一个接口,代表了一种跨进程通信的能力。只要实现了这个借口,这个对象就能跨进程传输。
  • IInterface : IInterface 代表的就是 Server 进程对象具备什么样的能力(能提供哪些方法,其实对应的就是 AIDL 文件中定义的接口)
  • Binder : Java 层的 Binder 类,代表的其实就是 Binder 本地对象。BinderProxy 类是 Binder 类的一个内部类,它代表远程进程的 Binder 对象的本地代理;这两个类都继承自 IBinder, 因而都具有跨进程传输的能力;实际上,在跨越进程的时候,Binder 驱动会自动完成这两个对象的转换。
  • Stub : AIDL 的时候,编译工具会给我们生成一个名为 Stub 的静态内部类;这个类继承了 Binder, 说明它是一个 Binder 本地对象,它实现了 IInterface 接口,表明它具有 Server 承诺给 Client 的能力;Stub 是一个抽象类,具体的 IInterface 的相关实现需要开发者自己实现。

5.2 AIDL实现过程

5.2.1 创建一个aidl文件在里面定义一个接口,接口继承自IInterface,里面定义服务端提供给客户端的方法
interface IHelloService {
    
    
   void sayHello();
   int sayHello_to(String name);
}
5.2.2 通过Android studio编译aidl文件,编译后生成对应的java文件
package com.example.binderdemo;
// Declare any non-default types here with import statements

public interface IHelloService extends android.os.IInterface
{
    
    
  /** Default implementation for IHelloService. */
  public static class Default implements com.example.binderdemo.IHelloService
  {
    
    
    @Override public void sayHello() throws android.os.RemoteException
    {
    
    
    }
    @Override public int sayHello_to(java.lang.String name) throws android.os.RemoteException
    {
    
    
      return 0;
    }
    @Override
    public android.os.IBinder asBinder() {
    
    
      return null;
    }
  }
  /** Local-side IPC implementation stub class. */
  public static abstract class Stub extends android.os.Binder implements com.example.binderdemo.IHelloService
  {
    
    
    private static final java.lang.String DESCRIPTOR = "com.example.binderdemo.IHelloService";
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
    
    
      this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an com.example.binderdemo.IHelloService interface,
     * generating a proxy if needed.
     */
    public static com.example.binderdemo.IHelloService asInterface(android.os.IBinder obj)
    {
    
    
      if ((obj==null)) {
    
    
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.example.binderdemo.IHelloService))) {
    
    
        return ((com.example.binderdemo.IHelloService)iin);
      }
      return new com.example.binderdemo.IHelloService.Stub.Proxy(obj);
    }
    @Override public android.os.IBinder asBinder()
    {
    
    
      return this;
    }
    @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
    {
    
    
      java.lang.String descriptor = DESCRIPTOR;
      switch (code)
      {
    
    
        case INTERFACE_TRANSACTION:
        {
    
    
          reply.writeString(descriptor);
          return true;
        }
        case TRANSACTION_sayHello:
        {
    
    
          data.enforceInterface(descriptor);
          this.sayHello();
          reply.writeNoException();
          return true;
        }
        case TRANSACTION_sayHello_to:
        {
    
    
          data.enforceInterface(descriptor);
          java.lang.String _arg0;
          _arg0 = data.readString();
          int _result = this.sayHello_to(_arg0);
          reply.writeNoException();
          reply.writeInt(_result);
          return true;
        }
        default:
        {
    
    
          return super.onTransact(code, data, reply, flags);
        }
      }
    }
    private static class Proxy implements com.example.binderdemo.IHelloService
    {
    
    
      private android.os.IBinder mRemote;
      Proxy(android.os.IBinder remote)
      {
    
    
        mRemote = remote;
      }
      @Override public android.os.IBinder asBinder()
      {
    
    
        return mRemote;
      }
      public java.lang.String getInterfaceDescriptor()
      {
    
    
        return DESCRIPTOR;
      }
      @Override public void sayHello() throws android.os.RemoteException
      {
    
    
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
    
    
          _data.writeInterfaceToken(DESCRIPTOR);
          boolean _status = mRemote.transact(Stub.TRANSACTION_sayHello, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
    
    
            getDefaultImpl().sayHello();
            return;
          }
          _reply.readException();
        }
        finally {
    
    
          _reply.recycle();
          _data.recycle();
        }
      }
      @Override public int sayHello_to(java.lang.String name) throws android.os.RemoteException
      {
    
    
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        int _result;
        try {
    
    
          _data.writeInterfaceToken(DESCRIPTOR);
          _data.writeString(name);
          boolean _status = mRemote.transact(Stub.TRANSACTION_sayHello_to, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
    
    
            return getDefaultImpl().sayHello_to(name);
          }
          _reply.readException();
          _result = _reply.readInt();
        }
        finally {
    
    
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      public static com.example.binderdemo.IHelloService sDefaultImpl;
    }
    static final int TRANSACTION_sayHello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_sayHello_to = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    public static boolean setDefaultImpl(com.example.binderdemo.IHelloService impl) {
    
    
      // Only one user of this interface can use this function
      // at a time. This is a heuristic to detect if two different
      // users in the same process use this function.
      if (Stub.Proxy.sDefaultImpl != null) {
    
    
        throw new IllegalStateException("setDefaultImpl() called twice");
      }
      if (impl != null) {
    
    
        Stub.Proxy.sDefaultImpl = impl;
        return true;
      }
      return false;
    }
    public static com.example.binderdemo.IHelloService getDefaultImpl() {
    
    
      return Stub.Proxy.sDefaultImpl;
    }
  }
  public void sayHello() throws android.os.RemoteException;
  public int sayHello_to(java.lang.String name) throws android.os.RemoteException;
}

这个java类首先声明了AIDL文件中定义的方法,同时声明了两个整形id用来标识这两个方法。接着声明了一个内部类Stub,这个Stub是一个binder类,
还定义了一个stub的内部代理类proxy。

public static abstract class Stub extends android.os.Binder implements com.example.binderdemo.IHelloService
  {
    
    
  //binder的唯一标识,一般用当前binder的类名表示
    private static final java.lang.String DESCRIPTOR = "com.example.binderdemo.IHelloService";
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
    
    
      this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an com.example.binderdemo.IHelloService interface,
     * generating a proxy if needed.
     */
     //该方法用于将服务端的binder对象转换为客户端所需AIDL接口类型的对象,这种转换过程区分进程,如果客户端和服务端位于同一进程,此方法返回的就是服务端的stub对象本身,否则返回的是系统封装后的stub.proxy对象
    public static com.example.binderdemo.IHelloService asInterface(android.os.IBinder obj)
    {
    
    
      if ((obj==null)) {
    
    
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.example.binderdemo.IHelloService))) {
    
    
        return ((com.example.binderdemo.IHelloService)iin);
      }
      return new com.example.binderdemo.IHelloService.Stub.Proxy(obj);
    }
    //此方法用于返回当前binder对象
    @Override public android.os.IBinder asBinder()
    {
    
    
      return this;
    }
    //该方法运行在服务端中的binder线程池中,当客户端发起跨进程请求时,远程请求通过系统底层封装后交给此方法处理。服务端通过code参数确定客户端所请求的目标方法是什么,接着从data中取出目标方法所需的参数(如果目标方法需要参数),然后执行目标方法,当目标方法执行完毕以后,就向reply中写入返回值(目标方法存在返回值),当此方法返回false时,表示客户端的请求失败,用此特征可以做权限验证。
    @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
    {
    
    
      java.lang.String descriptor = DESCRIPTOR;
      switch (code)
      {
    
    
        case INTERFACE_TRANSACTION:
        {
    
    
          reply.writeString(descriptor);
          return true;
        }
        case TRANSACTION_sayHello:
        {
    
    
          data.enforceInterface(descriptor);
          this.sayHello();
          reply.writeNoException();
          return true;
        }
        case TRANSACTION_sayHello_to:
        {
    
    
          data.enforceInterface(descriptor);
          java.lang.String _arg0;
          _arg0 = data.readString();
          int _result = this.sayHello_to(_arg0);
          reply.writeNoException();
          reply.writeInt(_result);
          return true;
        }
        default:
        {
    
    
          return super.onTransact(code, data, reply, flags);
        }
      }
    }
    //stub的代理类
    private static class Proxy implements com.example.binderdemo.IHelloService
    {
    
    
      private android.os.IBinder mRemote;
      Proxy(android.os.IBinder remote)
      {
    
    
        mRemote = remote;
      }
      @Override public android.os.IBinder asBinder()
      {
    
    
        return mRemote;
      }
      public java.lang.String getInterfaceDescriptor()
      {
    
    
        return DESCRIPTOR;
      }
      //这个方法运行在客户端,当客户端远程调用此方法时,它的内部实现是1.首先创建该方法所需要的参数信息写入到_data中,接着调用transact方法来发起RPC(远程过程调用)请求,同时当前线程挂起,然后服务端onTransact方法会被调用,直到RPC过程返回后,当前线程继承执行,并从_reply中取出rpc过程的返回结果,最后返回_reply中的数据
      @Override public void sayHello() throws android.os.RemoteException
      {
    
    
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
    
    
          _data.writeInterfaceToken(DESCRIPTOR);
          boolean _status = mRemote.transact(Stub.TRANSACTION_sayHello, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
    
    
            getDefaultImpl().sayHello();
            return;
          }
          _reply.readException();
        }
        finally {
    
    
          _reply.recycle();
          _data.recycle();
        }
      }
      @Override public int sayHello_to(java.lang.String name) throws android.os.RemoteException
      {
    
    
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        int _result;
        try {
    
    
          _data.writeInterfaceToken(DESCRIPTOR);
          _data.writeString(name);
          boolean _status = mRemote.transact(Stub.TRANSACTION_sayHello_to, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
    
    
            return getDefaultImpl().sayHello_to(name);
          }
          _reply.readException();
          _result = _reply.readInt();
        }
        finally {
    
    
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      public static com.example.binderdemo.IHelloService sDefaultImpl;
    }
    static final int TRANSACTION_sayHello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_sayHello_to = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    public static boolean setDefaultImpl(com.example.binderdemo.IHelloService impl) {
    
    
      // Only one user of this interface can use this function
      // at a time. This is a heuristic to detect if two different
      // users in the same process use this function.
      if (Stub.Proxy.sDefaultImpl != null) {
    
    
        throw new IllegalStateException("setDefaultImpl() called twice");
      }
      if (impl != null) {
    
    
        Stub.Proxy.sDefaultImpl = impl;
        return true;
      }
      return false;
    }
    public static com.example.binderdemo.IHelloService getDefaultImpl() {
    
    
      return Stub.Proxy.sDefaultImpl;
    }
  }

总结:

  • 文件里面包括了一个跨进程调用对象 Stub。Stub 继承 Binder, 说明它是一个 Binder 本地对象;实现 IInterface 接口,表明具有 Server 承诺给 Client 的能力;Stub 是一个抽象类,具体的 IInterface 的相关实现需要调用方自己实现
  • 在Stub 类中具有两个方法 asInterface 和 onTransact。asInterface,当 Client 端在创建和服务端的连接,调用 bindService 时需要创建一个 ServiceConnection 对象作为入参。在 ServiceConnection 的回调方法 onServiceConnected 中 会通过这个 asInterface(IBinder binder) 拿到 aidl 对象,这个 IBinder 类型的入参 binder 是驱动传给我们的,方法中会去调用 binder.queryLocalInterface() 去查找 Binder 本地对象,如果找到了就说明 Client 和 Server 在同一进程,那么这个 binder 本身就是 Binder 本地对象,可以直接使用。否则说明是 binder 是个远程对象,也就是 BinderProxy。因此需要我们创建一个代理对象 Proxy,通过这个代理对象来是实现远程访问。在java文件中也定义了代理对象 Proxy。正如前文所述 Proxy 是在 Stub 的 asInterface 中创建,能走到创建 Proxy 这一步就说明 Proxy 构造函数的入参是 BinderProxy,即这里的 remote 是个 BinderProxy 对象。最终通过一系列的函数调用,Client 进程通过系统调用陷入内核态,Client 进程中执行 addBook() 的线程挂起等待返回;驱动完成一系列的操作之后唤醒 Server 进程,调用 Server 进程本地对象的 onTransact()。最终又走到了 Stub 中的 onTransact() 中,onTransact() 根据函数编号调用相关函数(在 Stub 类中为接口中的每个函数中定义了一个编号,只不过上面的源码中我们简化掉了;在跨进程调用的时候,不会传递函数而是传递编号来指明要调用哪个函数);我们这个例子里面,调用了 Binder 本地对象的 addBook() 并将结果返回给驱动,驱动唤醒 Client 进程里刚刚挂起的线程并将结果返回。

猜你喜欢

转载自blog.csdn.net/qq_30193541/article/details/108547860
今日推荐