Binder的一次拷贝与通信原理

1 概述

Android系统中,涉及到多进程间的通信底层都是依赖于Binder IPC机制。例如当进程A中的Activity要向进程B中的Service通信,这便需要依赖于Binder IPC。不仅于此,整个Android系统架构中,大量采用了Binder机制作为IPC(进程间通信)方案。

当然也存在部分其他的IPC方式,如管道SystemVSocket等。那么Android为什么不使用这些原有的技术,而是要使开发一种新的叫Binder的进程间通信机制呢?

为什么要使用Binder?

  • 性能方面
    在移动设备上(性能受限制的设备,比如要省电),广泛地使用跨进程通信对通信机制的性能有严格的要求,Binder相对于传统的Socket方式,更加高效。Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,共享内存方式一次内存拷贝都不需要,但实现方式又比较复杂。

  • 安全方面
    传统的进程通信方式对于通信双方的身份并没有做出严格的验证,比如Socket通信ip地址是客户端手动填入,很容易进行伪造,而Binder机制从协议本身就支持对通信双方做身份校检,因而大大提升了安全性。

1.1 基本概念介绍

这里我们先从 Linux 中进程间通信涉及的一些基本概念开始介绍,然后逐步展开,向大家说明传统的进程间通信的原理。


上图展示了 Liunx 中跨进程通信涉及到的一些基本概念:

  • 进程隔离
    简单的说就是操作系统中,进程与进程间内存是不共享的。两个进程就像两个平行的世界,A 进程没法直接访问 B 进程的数据,这就是进程隔离的通俗解释。A 进程和 B 进程之间要进行数据交互就得采用特殊的通信机制:进程间通信(IPC)。

  • 进程空间划分:用户空间(User Space)/内核空间(Kernel Space)
    现在操作系统都是采用的虚拟存储器,对于 32 位系统而言,它的寻址空间(虚拟存储空间)就是 2 的 32 次方,也就是 4GB。操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也可以访问底层硬件设备的权限。为了保护用户进程不能直接操作内核,保证内核的安全,操作系统从逻辑上将虚拟空间划分为用户空间(User Space)和内核空间(Kernel Space)。针对 Linux 操作系统而言,将最高的 1GB 字节供内核使用,称为内核空间;较低的 3GB 字节供各进程使用,称为用户空间。

简单的说就是,内核空间(Kernel)是系统内核运行的空间,用户空间(User Space)是用户程序运行的空间。为了保证安全性,它们之间是隔离的。

  • 系统调用:用户态/内核态
    虽然从逻辑上进行了用户空间和内核空间的划分,但不可避免的用户空间需要访问内核资源,比如文件操作、访问网络等等。为了突破隔离限制,就需要借助系统调用来实现。系统调用是用户空间访问内核空间的唯一方式,保证了所有的资源访问都是在内核的控制下进行的,避免了用户程序对系统资源的越权访问,提升了系统安全性和稳定性。

    Linux 使用两级保护机制:0 级供系统内核使用,3 级供用户程序使用。

    当一个任务(进程)执行系统调用而陷入内核代码中执行时,称进程处于内核运行态(内核态)。此时处理器处于特权级最高的(0级)内核代码中执行。当进程处于内核态时,执行的内核代码会使用当前进程的内核栈。每个进程都有自己的内核栈。

    当进程在执行用户自己的代码的时候,我们称其处于用户运行态(用户态)。此时处理器在特权级最低的(3级)用户代码中运行。

    系统调用主要通过如下两个函数来实现:

  copy_from_user() //将数据从用户空间拷贝到内核空间
  copy_to_user() //将数据从内核空间拷贝到用户空间

1.2 Linux 下的传统 IPC 通信原理

理解了上面的几个概念,我们再来看看传统的 IPC 方式中,进程之间是如何实现通信的。

通常的做法是消息发送方将要发送的数据存放在内存缓存区中,通过系统调用进入内核态。然后内核程序在内核空间分配内存,开辟一块内核缓存区,调用 copyfromuser() 函数将数据从用户空间的内存缓存区拷贝到内核空间的内核缓存区中。同样的,接收方进程在接收数据时在自己的用户空间开辟一块内存缓存区,然后内核程序调用 copytouser() 函数将数据从内核缓存区拷贝到接收进程的内存缓存区。这样数据发送方进程和数据接收方进程就完成了一次数据传输,我们称完成了一次进程间通信。如下图:


这种传统的 IPC 通信方式有两个问题:

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

2. Binder 跨进程通信原理

理解了 Linux IPC 相关概念和通信原理,接下来我们正式介绍下 Binder IPC 的原理。

2.1 动态内核可加载模块 && 内存映射

正如前面所说,跨进程通信是需要内核空间做支持的。传统的 IPC 机制如管道、Socket 都是内核的一部分,因此通过内核支持来实现进程间通信自然是没问题的。但是 Binder 并不是 Linux 系统内核的一部分,那怎么办呢?这就得益于 Linux 的动态内核可加载模块(Loadable Kernel Module,LKM)的机制;模块是具有独立功能的程序,它可以被单独编译,但是不能独立运行。它在运行时被链接到内核作为内核的一部分运行。这样,Android 系统就可以通过动态添加一个内核模块运行在内核空间,用户进程之间通过这个内核模块作为桥梁来实现通信。

在 Android 系统中,这个运行在内核空间,负责各个用户进程通过 Binder 实现通信的内核模块就叫 Binder 驱动(Binder Dirver)

那么在 Android 系统中用户进程之间是如何通过这个内核模块(Binder 驱动)来实现通信的呢?难道是和前面说的传统 IPC 机制一样,先将数据从发送方进程拷贝到内核缓存区,然后再将数据从内核缓存区拷贝到接收方进程,通过两次拷贝来实现吗?显然不是,否则也不会有开篇所说的 Binder 在性能方面的优势了。

这就不得不通道 Linux 下的另一个概念:内存映射。

Binder IPC 机制中涉及到的内存映射通过 mmap() 来实现,mmap() 是操作系统中一种内存映射的方法。内存映射简单的讲就是将用户空间的一块内存区域映射到内核空间。映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间;反之内核空间对这段区域的修改也能直接反应到用户空间。

内存映射能减少数据拷贝次数,实现用户空间和内核空间的高效互动。两个空间各自的修改能直接反映在映射的内存区域,从而被对方空间及时感知。也正因为如此,内存映射能够提供对进程间通信的支持。

2.2 Binder IPC 实现原理

Binder IPC 正是基于内存映射(mmap)来实现的,但是 mmap() 通常是用在有物理介质的文件系统上的。

比如进程中的用户区域是不能直接和物理设备打交道的,如果想要把磁盘上的数据读取到进程的用户区域,需要两次拷贝(磁盘-->内核空间-->用户空间);通常在这种场景下 mmap() 就能发挥作用,通过在物理介质和用户空间之间建立映射,减少数据的拷贝次数,用内存读写取代I/O读写,提高文件读取效率。

而 Binder 并不存在物理介质,因此 Binder 驱动使用 mmap() 并不是为了在物理介质和用户空间之间建立映射,而是用来在内核空间创建数据接收的缓存空间。

一次完整的 Binder IPC 通信过程通常是这样:

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

3. Binder 通信模型

介绍完 Binder IPC 的底层通信原理,接下来我们看看实现层面是如何设计的。

一次完整的进程间通信必然至少包含两个进程,通常我们称通信的双方分别为客户端进程(Client)和服务端进程(Server),由于进程隔离机制的存在,通信双方必然需要借助 Binder 来实现。

3.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 驱动的交互来间接的实现跨进程通信。

Server创建了Binder实体,为其取一个字符形式,可读易记的名字,将这个Binder连同名字以数据包的形式通过Binder驱动发送给ServiceManager,通知ServiceManager注册一个名字为XX的Binder,它位于Server中。驱动为这个穿过进程边界的Binder创建位于内核中的实体结点以及ServiceManager对实体的引用,将名字以及新建的引用打包给ServiceManager。ServiceManager收数据包后,从中取出名字和引用填入一张查找表中。但是一个Server若向ServiceManager注册自己Binder就必须通过0这个引用和ServiceManager的Binder通信。Server向ServiceManager注册了Binder实体及其名字后,Client就可以通过名字获得该Binder的引用了。Clent也利用保留的0号引用向ServiceManager请求访问某个Binder:我申请名字叫XX的Binder的引用。ServiceManager收到这个连接请求,从请求数据包里获得Binder的名字,在查找表里找到该名字对应的条目,从条目中取出Binder引用,将该引用作为回复发送给发起请求的Client。

当然,不是所有的Binder都需要注册给ServiceManager广而告之的。Server端可以通过已经建立的Binder连接将创建的Binder实体传给Client,当然这条已经建立的Binder连接必须是通过实名Binder实现。由于这个Binder没有向ServiceManager注册名字,所以是匿名Binder。Client将会收到这个匿名Binder的引用,通过这个引用向位于Server中的实体发送请求。匿名Binder为通信双方建立一条私密通道,只要Server没有把匿名Binder发给别的进程,别的进程就无法通过穷举或猜测等任何方式获得该Binder的引用,向该Binder发送请求。

Client进程:使用服务的进程。

Server进程:提供服务的进程。

ServiceManager进程:ServiceManager的作用是将字符形式的Binder名字转化成Client中对该Binder的引用,使得Client能够通过Binder名字获得对Server中Binder实体的引用。

Binder驱动:驱动负责进程之间Binder通信的建立,Binder在进程之间的传递,Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。

Binder运行机制

图中Client/Server/ServiceManage之间的相互通信都是基于Binder机制。既然基于Binder机制通信,那么同样也是C/S架构,则图中的3大步骤都有相应的Client端与Server端。

注册服务(addService):Server进程要先注册Service到ServiceManager。该过程:Server是客户端,ServiceManager是服务端。

获取服务(getService):Client进程使用某个Service前,须先向ServiceManager中获取相应的Service。该过程:Client是客户端,ServiceManager是服务端。

使用服务:Client根据得到的Service信息建立与Service所在的Server进程通信的通路,然后就可以直接与Service交互。该过程:client是客户端,server是服务端。

图中的Client,Server,Service Manager之间交互都是虚线表示,是由于它们彼此之间不是直接交互的,而是都通过与Binder驱动进行交互的,从而实现IPC通信方式。其中Binder驱动位于内核空间,Client,Server,Service Manager位于用户空间。Binder驱动和Service Manager可以看做是Android平台的基础架构,而Client和Server是Android的应用层,开发人员只需自定义实现client、Server端,借助Android的基本平台架构便可以直接进行IPC通信。

Binder运行的实例解释

首先我们看看我们的程序跨进程调用系统服务的简单示例,实现浮动窗口部分代码:

//获取WindowManager服务引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);  
//布局参数layoutParams相关设置略...
View view=LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);  
//添加view
wm.addView(view, layoutParams);

注册服务(addService):在Android开机启动过程中,Android会初始化系统的各种Service,并将这些Service向ServiceManager注册(即让ServiceManager管理)。这一步是系统自动完成的。

获取服务(getService):客户端想要得到具体的Service直接向ServiceManager要即可。客户端首先向ServiceManager查询得到具体的Service引用,通常是Service引用的代理对象,对数据进行一些处理操作。即第2行代码中,得到的wm是WindowManager对象的引用。

使用服务:通过这个引用向具体的服务端发送请求,服务端执行完成后就返回。即第6行调用WindowManager的addView函数,将触发远程调用,调用的是运行在systemServer进程中的WindowManager的addView函数。

使用服务的具体执行过程


  • client通过获得一个server的代理接口,对server进行调用。
  • 代理接口中定义的方法与server中定义的方法时一一对应的。
  • client调用某个代理接口中的方法时,代理接口的方法会将client传递的参数打包成Parcel对象。
  • 代理接口将Parcel发送给内核中的binder driver。
  • server会读取binder driver中的请求数据,如果是发送给自己的,解包Parcel对象,处理并将结果返回。
  • 整个的调用过程是一个同步过程,在server处理的时候,client会block住。因此client调用过程不应在主线程

4. binder介绍

Android源码中,binder的核心库是在native层实现,但在java层和native层都有接口供应用程序使用。如果单从binder角度出发,Binder架构图如下:

(1) binder驱动层

Android因此添加了binder驱动,其设备节点为/dev/binder,主设备号为10,binder驱动程序在内核中的头文件和代码路径如下:
kernel/drivers/staging/binder.h
kernel/drivers/staging/binder.c

binder驱动层的主要作用是完成实际的binder数据传输

(2) binder adapter层

主要是IPCThreadState.cpp和ProcessState.cpp,源码位于frameworks/native/libs/binder目录下,这两个类都采用了单例模式,主要负责和驱动直接交互。

a、ProcessState,进程相关的类,负责打开binder设备,进行一些初始化设置并做内存映射;

void ProcessState::startThreadPool() 
该方法启动的新线程,并通过joinThreadPool读取binder设备,查看是否有请求

b、IPCThreadState,线程相关的类,负责直接和binder设备通信,使用ioctl读写binder驱动数据。

status_t IPCThreadState::talkWithDriver(bool doReceive) 
该方法调用的是ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)从/dev/binder读取。

status_t IPCThreadState::executeCommand(int32_t cmd)
该方法是对talkWithDriver返回的数据进行处理。

void IPCThreadState::joinThreadPool(bool isMain)
该方法创建线程并进行talkWithDriver以及executeCommand。

(3) Binder核心层

Binder核心层主要是IBinder及它的两个子类,即BBinder和BpBinder,分别代表了最基本的服务端及客户端。源码位于frameworks/native/libs/binder目录下。

binder service服务端实体类会继承BnInterface,而BnInterface会继承自BBinder,服务端可将BBinder对象注册到servicemananger进程。

客户端程序和驱动交互时只能得到远程对象的句柄handle,它可以调用调用ProcessState的getStrongProxyForHandle函数,利用句柄handle建立BpBinder对象,然后将它转为IBinder指针返回给调用者。这样客户端每次调用IBinder指针的transact方法,其实是执行BpBinder的transact方法。

(4) Binder框架层

a、Native Binder框架层包含以下类(frameworks/native/libs/binder):IInterface,BnInterface,BpInterface,等。

b、Java框架层包含以下类(frameworks/base/core/java/android/os):

IBinder,Binder,IInterface,ServiceManagerNative,ServiceManager,BinderInternal,IServiceManager,ServiceManagerProxy

Java框架层的类的部分方法的实现在本地代码里(frameworks/base/core/jni)。

4.1 Binder类分层

整个Binder从kernel至,native,JNI,Framework层所涉及的类如下:


【初始化】
Zygote启动时会有一个虚拟机注册过程,该过程调用AndroidRuntime::startReg方法来完成jni方法的注册。调用register_Android_os_Binder完成binder的native方法注册。

int register_Android_os_Binder(JNIEnv* env)
{
    // 注册Binder类的jni方法
    if (int_register_Android_os_Binder(env) < 0)  
        return -1;
 
    // 注册BinderInternal类的jni方法
    if (int_register_Android_os_BinderInternal(env) < 0)
        return -1;
 
    // 注册BinderProxy类的jni方法
    if (int_register_Android_os_BinderProxy(env) < 0)
        return -1;              
    ...
    return 0;
}

4.2 binder调用顺序

使用AIDL进行跨进程的通信,实际上就是使用binder,必须要继承binder接口,java和C++都有对应的IBinder,proxy,stub等,通过jni进程数据的交互,binder的核心在native层。整个调用关系如下。


5. Binder的Proxy-Stub模式

5.1 Java空间

IBinder/Binder/BinderProxy是Binder机制的核心api, 而IInterface和AIDL就是为了方便开发者使用Binder进行进程间通信。先看IBinder接口:

public interface IBinder 
{
    . . . . . .
    public String getInterfaceDescriptor() throws RemoteException;
    public boolean pingBinder();
    public boolean isBinderAlive();
    public IInterface queryLocalInterface(String descriptor);  //返回IInterface类型
    public void dump(FileDescriptor fd, String[] args) throws RemoteException;
    public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException;
    public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException;  //通信函数
    
    public interface DeathRecipient 
    {
        public void binderDied();
    }
    public void linkToDeath(DeathRecipient recipient, int flags)throws RemoteException;
    public boolean unlinkToDeath(DeathRecipient recipient, int flags);
}

Binder与BinderProxy的定义如下,都实现了IBinder接口

public class Binder implements IBinder {
 
    ...
    public Binder() {
        init();
 
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Binder> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Binder class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }
    }
    
    public void attachInterface(IInterface owner, String descriptor) {
        mOwner = owner;
        mDescriptor = descriptor;
    }
    
    public String getInterfaceDescriptor() {
        return mDescriptor;
    }
 
    public IInterface queryLocalInterface(String descriptor) {
        if (mDescriptor.equals(descriptor)) {
            return mOwner;
        }
        return null;
    }
 
    protected boolean onTransact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException {
        if (code == INTERFACE_TRANSACTION) {
            reply.writeString(getInterfaceDescriptor());
            return true;
        } else if (code == DUMP_TRANSACTION) {
            ParcelFileDescriptor fd = data.readFileDescriptor();
            String[] args = data.readStringArray();
            if (fd != null) {
                try {
                    dump(fd.getFileDescriptor(), args);
                } finally {
                    try {
                        fd.close();
                    } catch (IOException e) {
                        // swallowed, not propagated back to the caller
                    }
                }
            }
            // Write the StrictMode header.
            if (reply != null) {
                reply.writeNoException();
            } else {
                StrictMode.clearGatheredViolations();
            }
            return true;
        }
        return false;
    }
    
    public final boolean transact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException {
        if (false) Log.v("Binder", "Transact: " + code + " to " + this);
        if (data != null) {
            data.setDataPosition(0);
        }
        boolean r = onTransact(code, data, reply, flags);//调用onTransact()函数
        if (reply != null) {
            reply.setDataPosition(0);
        }
        return r;
    }
    
    public void linkToDeath(DeathRecipient recipient, int flags) {
    }
 
    public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
        return true;
    }
    
    protected void finalize() throws Throwable {
        try {
            destroy();
        } finally {
            super.finalize();
        }
    }
 
    private native final void init();
    private native final void destroy();
 
    // Entry point from android_util_Binder.cpp's onTransact
    private boolean execTransact(int code, long dataObj, long replyObj, int flags) {
        ...
        try {
            res = onTransact(code, data, reply, flags);//调用onTransact()函数
        } catch (RemoteException e) {
            if ((flags & FLAG_ONEWAY) != 0) {
                Log.w(TAG, "Binder call failed.", e);
            } else {
                reply.setDataPosition(0);
                reply.writeException(e);
            }
            res = true;
        } 
        ...
        return res;
    }
}
 
final class BinderProxy implements IBinder {
    ...
    public IInterface queryLocalInterface(String descriptor) {
        return null;
    }
 
    public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
        return transactNative(code, data, reply, flags);
    }
 
    public native String getInterfaceDescriptor() throws RemoteException;
    public native boolean transactNative(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException;
    public native void linkToDeath(DeathRecipient recipient, int flags)
            throws RemoteException;
    public native boolean unlinkToDeath(DeathRecipient recipient, int flags);
 
    BinderProxy() {
        mSelf = new WeakReference(this);
    }    
   ...
}

最后,在来看IInterface接口,很简单,就一个方法,返回值是IBinder。

public interface IInterface
{
    public IBinder asBinder();
}

四者的UML类图如下所示:


这就是BInder机制Proxy-Stub模式原始配方:
在这个类图的最顶层,有两个接口,IInterface和IBinder。IBinder代表跨进程传输的能力,而IInterface则用来辅助实现具体的传输业务。Binder是IBinder的实现类,因此它具备跨进程传输的能力,它实际上就是远程Server端的Binder对象本身。与此对应的BinderProxy则是远程Binder的代理对象,给Client进程用的。在跨越进程的时候,Binder驱动会自动完成这两个对象的转换。

当我们写了一个AIDL文件之后,如下:

interface ICompute {
    int add(int a,int b);
}

此时,会生成一个相应的java文件,如下:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: D:\\Code\\Github\\Android\\Demo\\src\\com\\tfygg\\demo\\service\\ICompute.aidl
 */
package com.tfygg.demo.service;
 
public interface ICompute extends android.os.IInterface {
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements com.tfygg.demo.service.ICompute {
        private static final java.lang.String DESCRIPTOR = "com.tfygg.demo.service.ICompute";
 
        /** Construct the stub at attach it to the interface. */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }
 
        /**
         * Cast an IBinder object into an com.tfygg.demo.service.ICompute
         * interface, generating a proxy if needed.
         */
        public static com.tfygg.demo.service.ICompute asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.tfygg.demo.service.ICompute))) {
                return ((com.tfygg.demo.service.ICompute) iin);
            }
            return new com.tfygg.demo.service.ICompute.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 {
            switch (code) {
            case INTERFACE_TRANSACTION: {
                reply.writeString(DESCRIPTOR);
                return true;
            }
            case TRANSACTION_add: {
                data.enforceInterface(DESCRIPTOR);
                int _arg0;
                _arg0 = data.readInt();
                int _arg1;
                _arg1 = data.readInt();
                int _result = this.add(_arg0, _arg1);
                reply.writeNoException();
                reply.writeInt(_result);
                return true;
            }
            }
            return super.onTransact(code, data, reply, flags);
        }
 
        private static class Proxy implements com.tfygg.demo.service.ICompute {
            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 int add(int a, int b) 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.writeInt(a);
                    _data.writeInt(b);
                    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }
 
        static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }
 
    public int add(int a, int b) throws android.os.RemoteException;
}

对照着生成出来的ICompute.java文件,绘制如下Binder模型图:

上图中绿色部分就是生成的java文件的内容,我们发现AIDL的Binder模型是基于原始配方的扩展。当我们写完ICompute.aidl之后,ICompute,Stub,Proxy已经自动生成出来,其作用如下:
(1)ICompute接口继承了IInterface,并写了add(a,b)的方法,代表Server端进程具备计算两数相加的能力。此方法将在Stub的具体实现类中重写。
(2)Stub是一个抽象类,他本质是一个Binder,他存在的目的之一是约定好asInterface,asBinder,onTransact方法,减少我们开发具体Binder对象的工作量。此外,Stub即需要跨进程传输,又需要约定远端服务端具备的能力,因此他需要同时实现IInterface和IBinder接口,通过上文的类图可以看得出来。
(3)Proxy是代理对象,它在Client进程中使用,它持有BinderProxy对象,BinderProxy能帮我们访问服务端Binder对象的能力。

5.2 C空间

在C空间中,仍然是一样的Binder配方,不同的是,C空间没有了AIDL,而是使用模板辅助实现了Proxy-stub。所以,在C空间中也有IBinder.h,Binder。UML类图如下:


先看IBinder,其定义截选如下:

class IBinder : public virtual RefBase
{
public:
    . . . . . .
    IBinder();
    virtual sp<IInterface>  queryLocalInterface(const String16& descriptor);
    virtual const String16& getInterfaceDescriptor() const = 0;
 
    virtual bool            isBinderAlive() const = 0;
    virtual status_t        pingBinder() = 0;
    virtual status_t        dump(int fd, const Vector<String16>& args) = 0;
    virtual status_t        transact(uint32_t code, const Parcel& data,
                                     Parcel* reply, uint32_t flags = 0) = 0;
 
    class DeathRecipient : public virtual RefBase
    {
    public:
        virtual void binderDied(const wp<IBinder>& who) = 0;
    };
    virtual status_t        linkToDeath(const sp<DeathRecipient>& recipient,
                                        void* cookie = NULL, uint32_t flags = 0) = 0;
    virtual status_t        unlinkToDeath(const wp<DeathRecipient>& recipient,
                                          void* cookie = NULL, uint32_t flags = 0,
                                          wp<DeathRecipient>* outRecipient = NULL) = 0;
 
    virtual bool            checkSubclass(const void* subclassID) const;
    
    typedef void (*object_cleanup_func)(const void* id, void* obj, void* cleanupCookie);
    virtual void            attachObject(const void* objectID, void* object,
                                         void* cleanupCookie, object_cleanup_func func) = 0;
    virtual void*           findObject(const void* objectID) const = 0;
    virtual void            detachObject(const void* objectID) = 0;
 
    virtual BBinder*        localBinder();
    virtual BpBinder*       remoteBinder();
 
protected:
    virtual          ~IBinder();
private:
};

有接口必然有实现,BBinder和BpBinder都是IBinder的实现类,这里就总结一下他们的区别:

(1)pingBinder, BBinder直接返回OK,而BpBinder需要运行一个transact函数,这个函数很重要。
(2)linkToDeath()是用来在服务挂的时候通知客户端的,那服务端当然不需要自己监视自己咯,所以BBinder直接返回非法,而Bpbinder需要通过requestDeathNotification()要求某人完成这个事情。

奇怪的是BBinder和BpBinder都没有实现queryLocalInterface() 接口啊,那肯定另有他人实现这个类了,这个人就是IInterface.h。客户程序通过queryLocalInterface() 可以知道服务端都提供哪些服务。
在IInterface.h中定义的BnInterface和BpInterface是两个重要的模版,这是为各种程序中使用的。

template  
class BnInterface : public INTERFACE, public BBinder  
{  
public:  
    virtual sp  queryLocalInterface(const String16& _descriptor);  
    virtual String16        getInterfaceDescriptor() const;  
protected:  
    virtual IBinder*        onAsBinder();  
};  
     BnInterface模版的定义如下所示:  
template  
class BpInterface : public INTERFACE, public BpRefBase  
{  
public:  
                            BpInterface(const sp& remote);  
protected:  
    virtual IBinder*    onAsBinder();  
};  

这两个模版在使用的时候,起到得作用实际上都是双继承:使用者定义一个接口INTERFACE,然后使用BnInterface和BpInterface两个模版结合自己的接口,构建自己的BnXXX和BpXXX两个类。
DECLARE_META_INTERFACE和IMPLEMENT_META_INTERFACE两个宏用于帮助BpXXX类的实现:

#define DECLARE_META_INTERFACE(INTERFACE)                               /  
    static const String16 descriptor;                                   /  
    static sp asInterface(const sp& obj);        /  
    virtual String16 getInterfaceDescriptor() const;                    /  
#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       /  
    const String16 I##INTERFACE::descriptor(NAME);                      /  
    String16 I##INTERFACE::getInterfaceDescriptor() const {             /  
        return I##INTERFACE::descriptor;                                /  
    }                                                                   /  
    sp I##INTERFACE::asInterface(const sp& obj)  /  
    {                                                                   /  
        sp intr;                                          /  
        if (obj != NULL) {                                              /  
            intr = static_cast(                          /  
                obj->queryLocalInterface(                               /  
                        I##INTERFACE::descriptor).get());               /  
            if (intr == NULL) {                                         /  
                intr = new Bp##INTERFACE(obj);                          /  
            }                                                           /  
        }                                                               /  
        return intr;                                                    /  
    }  

在定义自己的类的时候,只需要使用DECLARE_META_INTERFACE和IMPLEMENT_META_INTERFACE两个接口,并结合类的名称,就可以实现BpInterface中asInterface()和getInterfaceDescriptor()两个函数。
此外,IInterface还起到了转换的作用,IXXXService继承自IInterface,而IInterface中的asBinder()方法,会将自身,也就是IXXXService转换成一个IBinder对象,而asInterface接口则是将IIBinder转换为IXXXService接口。

template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
return INTERFACE::asInterface(obj);
}

由此可知,使用interface_cast的作用就是将IBinder实例转化成IXXXService实例。

可以看出,C空间的IBinder接口和java空间类似,下图可以看出C空间的binder的Proxy-stub

部分摘自
https://blog.csdn.net/augfun/article/details/82343249
https://blog.csdn.net/AndroidStudyDay/article/details/93749470
https://blog.csdn.net/tfygg/article/details/51626632

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容