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次拷贝即可)。
二、几个重要的概念
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 进程间通讯机制

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

推荐阅读更多精彩内容