Android Binder机制扫盲

由于Android系统保护机制(沙箱机制),两个进程是各自运行在自己的进程空间之中的,相互之间进行隔离并不能够直接进行通讯(确保一个进程挂掉了,不会影响另外一个进程的运行)。

一、为什么使用Binder机制

Android系统是基于Linux系统的,我们知道Linux 系统之中常见的进程之间的通讯就有共享内存、消息队列、管道、Socket等。那为什么Android 系统不直接采用Linux 的进程之间的通讯方式,而是采用Binder机制,C/S模式来实现进程之间的通讯呢?

我想主要有以下三个方面的原因。

  • C/S架构模式,Binder能够很好的支持Client-Server模式,相比只有Socket能够提供C/S模式,但是Socket主要是用于网络之间的通讯以及本地进程之间的低速通讯,效率太低。
  • 安全考虑, Android 系统为每一个应用程序都分配了唯一的一个UID,且Android系统安全权限管理很严格,Server端会根据权限控制策略,判断UID/PID是否满足访问权限。
  • 性能考虑, 传统的消息队列、管道、Socke都需要通过2次拷贝操作,才能实现从用户态空间到内核空间的数据拷贝。而共享内存实现方式复杂,没有客户与服务端之别, 需要充分考虑到访问临界资源的并发同步问题,否则可能会出现死锁等问题。而Binder只需要一次拷贝操作就可以(从发送方的缓存区拷贝到内核的缓存区,而接收方的缓存区与内核的缓存区是映射到同一块物理地址的,因此只需要1次拷贝即可)。
二、几个重要的概念

《Android Binder机制扫盲》 Binder中各角色之间关系

  • Binder实体,其实就是Server在内核中的binder_node结构体的对象,保存着Server对象在用户空间的地址信息。通过Binder实体可以找到用户空间的Server的对象。
  • Binder引用,其实就是是内核中binder_ref结构体的对象,它的作用是在表示”Binder实体”的引用。简单说是每一个Binder引用都是某一个Binder实体的引用,通过Binder引用可以在内核中找到它对应的Binder实体。
  • 远程服务,Server都是以服务的形式注册到ServiceManager中进行管理的,可以理解就是Server提供的服务。
  • ServiceManager,是用户空间的守护进程,由init进程负责启动,之后会打开/dev/binder设备,建立128K虚拟内存映射Binder 驱动,下发BINDER_SET_CONTEXT_MGR的command,声明自己成为上下文管理者,进入binder消息轮询,等待client消息到来。
  • Server注册服务,Server首先会向Binder驱动发起注册服务请求,Binder驱动会新建与该Server对应的Binder实体,在ServiceManager的保存Binder引用的红黑树中查找Server的Binder引用如果不存在则会新建一个与该Server对应的Binder引用。并将其添加到ServiceManager的保存Binder引用的红黑树之中。
  • Client获取远程服务,Client携带Server的服务名向Binder驱动获取远程服务,Binder驱动会转发会给ServiceManager进程,且有ServiceManager返回Server对应的Binder实体的Binder引用信息,Client根据这个信息创建Server的远程服务,该远程服务会对应的通过Binder驱动和真正的Server进行交互,从而执行相应的动作。
三、AIDL 示例

在Android开发过程中,我们一般是采用编写AIDL文件的形式来描述服务器提供哪些接口。
IDataManager.aidl 文件

// 无论应用的类是否和aidl文件在同一包下,都需要显示import
import org.github.lion.aidl_demo.Data;
interface IDataManager {
    /** AIDL 支持的数据类型划分为四类
     * 第一类是 Java 编程语言中的基本类型
     * 第二类包括 String、List、Map 和 CharSequence
     * 第三类是其他 AIDL 生成的 interface
     * 第四类是实现了 Parcelable protocol 的自定义类
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean
                    , float aFloat, double aDouble, String aString);
    int getDataTypeCount();
    List<Data> getData();
    String getUrlContent(String url);
}

自定义实现Parcelable接口类型

/** 必须实现Parcelable接口
 *  1. 定义CREATOR对象
 */
public class Data implements Parcelable {

    ...

    protected Data(Parcel in) {
        ...
    }

    public static final Creator<Data> CREATOR = new Creator<Data>() {
        @Override
        public Data createFromParcel(Parcel in) {
            return new Data(in);
        }

        @Override
        public Data[] newArray(int size) {
            return new Data[size];
        }
    };
}

服务端的实现

private static final IDataManager.Stub mBinder = new IDataManager.Stub() {

    @Override
    public void basicTypes(int anInt, long aLong, boolean aBoolean
                           , float aFloat, double aDouble, String aString) 
                           throws RemoteException {
    }

    @Override
    public int getDataTypeCount() throws RemoteException {
        // todo return some data
        return 0;
    }

    @Override
    public List<Data> getData() throws RemoteException {
        // todo return some data
        return null;
    }

    @Override
    public String getUrlContent(String url) throws RemoteException {
        // todo return some data
        return null;
    }
};

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

客户端实现

private IDataManager dataManagerService = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    // 绑定服务
    bindService(new Intent(this, DataManagerService.class), dataServiceConnection,
            Context.BIND_AUTO_CREATE);
}

private ServiceConnection dataServiceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {       
        // 返回IBinder 对象,封装为代理类
        dataManagerService = IDataManager.Stub.asInterface(service);
    }
    @Override
    public void onServiceDisconnected(ComponentName name) {
        dataManagerService = null;
    }
};

接下来我们看看Android Studio帮我们自动生成的Java文件里面的内容。

// IDataManager2 直接继承了IInterface
public interface IDataManager2 extends IInterface {
    // 返回值为基本数据类型,定义接口时不需要做特殊处理
    int getDataCount() throws RemoteException;
    // 自定义的返回数据类型需要实现Parcelable接口,进程间通信不能直接共享内存,需要将对象持久化。
    // 所以自定义的类需要实现Parcelable接口
    List<Data2> getData() throws RemoteException;
}

//内部抽象类集成Binder实现了IDataManager2(Stub)接口
public abstract class DataManagerNative extends Binder implements IDataManager2 {

    // Binder描述符,唯一标识符
    private static final String DESCRIPTOR = "com.github.onlynight.aidl_demo2.aidl.IDataManager2";

    // 每个方法对应的ID
    private static final int TRANSACTION_getDataCount = IBinder.FIRST_CALL_TRANSACTION;
    private static final int TRANSACTION_getData = IBinder.FIRST_CALL_TRANSACTION + 1;

    public DataManagerNative() {
        attachInterface(this, DESCRIPTOR);
    }

    /**
     * 将Binder转化为IInterface接口
     *
     * @param binder
     * @return
     */
    public static IDataManager2 asInterface(IBinder binder) {
        if (binder == null) {
            return null;
        }
        //同一进程内直接返回
        IInterface iin = binder.queryLocalInterface(DESCRIPTOR);
        if ((iin != null) && (iin instanceof IDataManager2)) {
            return (IDataManager2) iin;
        }

        //不在同一进程使用代理获取远程服务
        return new Proxy(binder);
    }

    @Override
    public IBinder asBinder() {
        return this;
    }

    /**
     * 我们查看Binder的源码就可以看出实际上transact方法真正的执行体
     * 是这个onTransact方法。
     *
     * @param code  服务器回掉的方法ID,每一个方法都有一个唯一id,
     *              这样方法回调时可通过id判断回调的方法。
     * @param data  输入的参数,传递给服务端的参数
     * @param reply 输出的参数,服务器返回的数据
     * @param flags 默认传入0
     * @return
     * @throws RemoteException 远端服务器无响应抛出该错误。
     */
    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        switch (code) {
            case TRANSACTION_getDataCount: {
                data.enforceInterface(DESCRIPTOR);
                int _result = this.getDataCount();
                reply.writeNoException();
                reply.writeInt(_result);
                return true;
            }
            case TRANSACTION_getData: {
                data.enforceInterface(DESCRIPTOR);
                List<Data2> _result = this.getData();
                reply.writeNoException();
                reply.writeTypedList(_result);
                return true;
            }
        }
        return super.onTransact(code, data, reply, flags);
    }

    /**
     * 代理类,调用transact方法。
     */
    private static class Proxy implements IDataManager2 {

        private IBinder remote;

        Proxy(IBinder remote) {
            this.remote = remote;
        }

        public String getInterfaceDescriptor() {
            return DESCRIPTOR;
        }

        @Override
        public int getDataCount() throws RemoteException {
            // 输入参数
            Parcel _data = Parcel.obtain();

            //输出参数
            Parcel _reply = Parcel.obtain();
            int _result;
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                remote.transact(TRANSACTION_getDataCount, _data, _reply, 0);
                _reply.readException();
                _result = _reply.readInt();
            } finally {
                _reply.recycle();
                _data.recycle();
            }
            return _result;
        }

        @Override
        public List<Data2> getData() throws RemoteException {
            Parcel _data = Parcel.obtain();
            Parcel _reply = Parcel.obtain();
            List<Data2> _result;
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                remote.transact(TRANSACTION_getData, _data, _reply, 0);
                _reply.readException();
                _result = _reply.createTypedArrayList(Data2.CREATOR);
            } finally {
                _reply.recycle();
                _data.recycle();
            }
            return _result;
        }

        @Override
        public IBinder asBinder() {
            return remote;
        }
    }
}

参考博客:
1. binder守护进程servicemanager简介
2. Android Binder机制(一) Binder的设计和框架
3. 一篇文章了解相见恨晚的 Android Binder 进程间通讯机制

    原文作者:01_小小鱼_01
    原文地址: https://www.jianshu.com/p/620a42759e8b
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞