Android IBinder机制

Android系统Binder机制中的四个组件Client、Server、Service Manager和Binder驱动程序的关系如下图所示:

  1. Client、Server和Service Manager实现在用户空间中,Binder驱动程序实现在内核空间中

  2. Binder驱动程序和Service Manager在Android平台中已经实现,开发者只需要在用户空间实现自己的Client和Server

  3. Binder驱动程序提供设备文件/dev/binder与用户空间交互,Client、Server和Service Manager通过open和ioctl文件操作函数与Binder驱动程序进行通信

  4. Client和Server之间的进程间通信通过Binder驱动程序间接实现

  5. Service Manager是一个守护进程,用来管理Server,并向Client提供查询Server接口的能力

Android 整体架构

我们先来大概看下 Android 这座大山的整体轮廓。我们先从 Android 的整体架构来看看 Binder 是处于什么地位,这张图引自 Android 项目开源网站:https://source.android.com:

从下往上依次为

  • 内核层:Linux 内核和各类硬件设备的驱动,这里需要注意的是,Binder IPC 驱动也是在这一层实现,比较特殊
  • 硬件抽象层:封装「内核层」硬件驱动,提供可供「系统服务层」调用的统一硬件接口
  • 系统服务层:提供核心服务,并且提供可供「应用程序框架层」调用的接口
  • Binder IPC 层:作为「系统服务层」与「应用程序框架层」的 IPC 桥梁,互相传递接口调用的数据,实现跨进层的通讯
  • 应用程序框架层:这一层可以理解为 Android SDK,提供四大组件,View 绘制体系等平时开发中用到的基础部件

在一个大的项目里面,分层是非常重要的,处于最底层的接口最具有「通用性」,接口粒度最细,越往上层通用性降低。理论上来说上面的每一层都可以「开放」给开发者调用,例如开发者可以直接调用硬件抽象层的接口去操作硬件,或者直接调用系统服务层中的接口去直接操作系统服务,甚至是像 Windows 开发一样,开发者可以在内核层写程序,运行在内核中。不过开放带来的问题就是开发者权利太大,对于系统的稳定性是没有任何好处的,一个病毒制作者写了一个内核层的病毒,系统也许永远也起不来了。所以谷歌的做法是将开发者的权利收拢到了「应用程序框架层」,开发者只能调用这一层提供的接口。

上面的层次中,内核层与硬件抽象层均用 C/C++ 实现,系统服务层是以 Java 实现,硬件抽象层编译为 so 文件,以 JNI 的形式供系统服务层使用。系统服务层中的服务随系统的启动而启动,只要不关机,就会一直运行。这些服务干什么事情呢?其实很简单,就是完成一个手机该有的核心功能如短信的收发管理、电话的接听、挂断以及应用程序的包管理、Activity 的管理等等。每一个服务均运行在一个独立进程中,因为是以 Java 实现,所以本质上来说就是运行在一个独立进程的 Dalvik 虚拟机中。问题就来了,开发者的 APP 运行在一个新的进程空间,如何调用到系统服务层中的接口呢?答案是 IPC(Inter-Process Communication),进程间通讯,缩写与 RPC(Remote Procedure Call)是不一样的,实现原理也是不一样的。每一个系统服务在应用层序框架层都有一个 Manager 与之对应,方便开发者调用其相关的功能,具体关系大致如下

IPC 的方式有很多种,例如 socket、共享内存、管道、消息队列等等,我们就不去深究为何要使用 Binder 而不使用其他方式去做,到目前为止,这座大山的面目算是有个大概的轮廓了。

小结

  • Android 从下而上分了内核层、硬件抽象层、系统服务层、Binder IPC 层、应用程序框架层
  • Android 中「应用程序框架层」以 SDK 的形式开放给开发者使用,「系统服务层」中的核心服务随系统启动而运行,通过应用层序框架层提供的 Manager 实时为应用程序提供服务调用。系统服务层中每一个服务运行在自己独立的进程空间中,应用程序框架层中的 Manager 通过 Binder IPC 的方式调用系统服务层中的服务。

Binder IPC 的架构

下面我们就来看看 Binder IPC 的架构是怎样的

Binder IPC 属于 C/S 结构,Client 部分是用户代码,用户代码最终会调用 Binder Driver 的 transact 接口,Binder Driver 会调用 Server,这里的 Server 与 service 不同,可以理解为 Service 中 onBind 返回的 Binder 对象,请注意区分下:

  • Client:用户需要实现的代码,如 AIDL 自动生成的接口类
  • Binder Driver:在内核层实现的 Driver
  • Server:这个 Server 就是 Service 中 onBind 返回的 IBinder 对象

需要注意的是,上面绿色的色块部分都是属于用户需要实现的部分,而蓝色部分是系统去实现了。也就是说 Binder Driver 这块并不需要知道,Server 中会开启一个线程池去处理客户端调用。为什么要用线程池而不是一个单线程队列呢?试想一下,如果用单线程队列,则会有任务积压,多个客户端同时调用一个服务的时候就会有来不及响应的情况发生,这是绝对不允许的。

对于调用 Binder Driver 中的 transact 接口,客户端可以手动调用,也可以通过 AIDL 的方式生成的代理类来调用,服务端可以继承 Binder 对象,也可以继承 AIDL 生成的接口类的 Stub 对象。

切记,这里 Server 的实现是线程池的方式,而不是单线程队列的方式,区别在于,单线程队列的话,Server 的代码是线程安全的,线程池的话,Server 的代码则不是线程安全的,需要开发者自己做好多线程同步。

小结

  • Binder IPC 属于 C/S 架构,包括 Client、Driver、Server 三个部分
  • Client 可以手动调用 Driver 的 transact 接口,也可以通过 AIDL 生成的 Proxy 调用
  • Server 中会启动一个「线程池」来处理 Client 的调用请求,处理完成后将结果返回给 Driver,Driver 再返回给 Client

这里就回答了两个问题:Service 中通过 AIDL 提供的接口并不是线程安全的,同理 ContentProvider 底层也是使用 Binder,同样不是线程安全的,至于是否需要做多线程保护,看业务而定,最好是做好多线程同步,以防万一。

使用 AIDL 实现 Binder IPC

Android 给了我们更好用的方式那就是 AIDL,假如我们要做一个上报数据的功能,运行在 Service 中,在后台上报数据,接口定义如下

IReporter.aidl

package com.android.binder;

interface IReporter {

    int report(String values, int type);
}

Server

AidlService.java

public class AidlService extends Service {

    public static final class Reporter extends IReporter.Stub {

        @Override
        public int report(String values, int type) throws RemoteException {
            return type;
        }
    }

    private Reporter mReporter;

    public AidlService() {
        mReporter = new Reporter();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mReporter;
    }
}

继承了 AIDL 自动生成的 Stub 对象,它是什么呢?我们可以看下它的定义

IReporter.java

public interface IReporter extends android.os.IInterface
{

    public static abstract class Stub extends android.os.Binder implements com.android.binder.IReporter {
        ...

        @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_report:
                {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    int _arg1;
                    _arg1 = data.readInt();
                    int _result = this.report(_arg0, _arg1);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }
    }

...

}

自动生成的 IReporter 类自动给我们处理了一些参数的组包和解包而已,在 case 语句中调用了 this.report 即可调用到自己的业务逻辑部分了。

Driver

该部分已经被 Binder 类给封装了,暴露给开发者的已经是很简单的使用方式了,即继承 Binder,实现 onTransact 即可。

Client

MainActivity.java

private IReporter mReporterAidl;

private class AidlConnection implements ServiceConnection {

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        mReporterAidl = IReporter.Stub.asInterface(service);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        mReporterAidl = null;
    }
}

...

@Override
protected void onCreate(Bundle savedInstanceState) {

    ...

    Intent intent = new Intent(this, AidlService.class);
    bindService(intent, new AidlConnection(), BIND_AUTO_CREATE);
}

这里调用了 Stub 对象的 asInterface,具体做了什么呢?

public static com.android.binder.IReporter asInterface(android.os.IBinder obj)
{
    if ((obj==null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin!=null)&&(iin instanceof com.android.binder.IReporter))) {
        return ((com.android.binder.IReporter)iin);
    }
    return new com.android.binder.IReporter.Stub.Proxy(obj);
}

先查找本地接口是否存在,判断是否是本地调用,如果是则直接返回 IReporter 的对象,否则返回 Stub.Proxy 对象,这个 Proxy 对象是做什么的呢?

private static class Proxy implements com.android.binder.IReporter
{
    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 report(java.lang.String values, int type) 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(values);
            _data.writeInt(type);
            mRemote.transact(Stub.TRANSACTION_report, _data, _reply, 0);
            _reply.readException();
            _result = _reply.readInt();
        }
        finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }
}

基本上已经很明了了,就是一个代理对象,对调用接口参数做组包而已,然后调用了 mRemote.transact 接口。

小结

  • AIDL 自动生成了 Stub 类
  • 在 Service 端继承 Stub 类,Stub 类中实现了 onTransact 方法实现了「解包」的功能
  • 在 Client 端使用 Stub 类的 Proxy 对象,该对象实现了「组包」并且调用 transact 的功能

有了 AIDL 之后,IReporter 接口就变得有意义了,Client 调用接口,Server 端实现接口,一切「组包」、「解包」的逻辑封装在了 Stub 类中,一切就是那么完美。

参考资料

https://www.jianshu.com/p/bdef9e3178c9
https://blog.csdn.net/luoshengyang/article/details/6618363
Android IBinder机制简单介绍