Binder的使用和上层原理

简单介绍

Binder是Android中的一个类,实现了IBinder接口。从IPC角度说,Binder是Andoird的一种跨进程通讯方式。从Android Framework角度来说,Binder是ServiceManager连接各种Manager(ActivityManager·、WindowManager)和相应ManagerService的桥梁。从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当bindService时,服务端返回一个包含服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务器端提供的服务或者数据(包括普通服务和基于AIDL的服务)。

Android中Binder主要用于Service,包括AIDL和Messenger。普通Service的Binder不涉及进程间通信,Messenger的底层其实是AIDL,所以下面通过AIDL分析Binder的工作机制。

由系统根据AIDL文件自动生成.java文件

  • Book.java
    表示图书信息的实体类,实现了Parcelable接口。
  • Book.aidl
    Book类在AIDL中的声明。

    1
    2
    package tk.thinkerzhangyan.ipcapplication;
    parcelable Book;
  • IBookManager.aidl

    1
    2
    3
    4
    5
    6
    7
    package tk.thinkerzhangyan.ipcapplication;
    import tk.thinkerzhangyan.ipcapplication.Book;

    interface IBookManager {
    List<Book> getBookList();
    void addBook(in Book book);
    }

定义的管理Book实体的一个接口,包含getBookList和addBook两个方法,虽然IBookManager接口和Book实体在一个包下面,但是还是需要用import进行引入。

系统为IBookManager.aidl生产的Binder类,在gen目录下的IBookManager.java类。

IBookManager继承了IInterface接口,所有在Binder中传输的接口都需要继承IInterface接口。结构如下:

  1. 声明了getBookListaddBook方法,还声明了两个整型id分别标识这两个方法,用于标识在transact过程中客户端请求的到底是哪个方法。
  2. 声明了一个内部类Stub,这个Stub就是一个Binder类,当客户端和服务端位于同一进程时,方法调用不会走跨进程的transact。当二者位于不同进程时,方法调用需要走transact过程,这个逻辑有Stub的内部代理类Proxy来完成。
  3. 这个接口的核心实现就是它的内部类StubStub的内部代理类Proxy

Stub和Proxy类的内部方法和定义

  1. DESCRIPTOR
    Binder的唯一标识,一般用Binder的类名表示。
  2. asInterface(android.os.IBinder obj)
    将服务端的Binder对象转换为客户端所需的AIDL接口类型的对象,如果C/S位于同一进程,此方法返回就是服务端的Stub对象本身,否则返回的就是系统封装后的Stub.proxy对象。
  3. asBinder
    返回当前Binder对象。
  4. onTransact
    这个方法运行在服务端的Binder线程池中,由客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。该方法的原型是
    java public Boolean onTransact(int code,Parcelable data,Parcelable reply,int flags)
    Ⅰ· 服务端通过code确定客户端请求的目标方法是什么,
    Ⅱ· 接着从data取出目标方法所需的参数,然后执行目标方法。
    Ⅲ·执行完毕后向reply写入返回值(如果有返回值)。
    Ⅳ·如果这个方法返回值为false,那么服务端的请求会失败,利用这个特性我们可以来做权限验证,毕竟我们不希望每一个进程都能远程调用我们的服务。
  5. Proxy#getBookList和Proxy#addBook
    这两个方法运行在客户端,当客户端远程调用这两个方法的时候,其内部实现过程是这样的:
    Ⅰ· 首先创建该方法所需要的输入型对象Parcel对象_data,输出型Parcel对象_reply和返回值对象List。
    Ⅱ· 然后把该方法的参数信息写入_data(如果有参数)
    Ⅲ· 接着调用transact方法发起RPC(远程过程调用),同时当前线程挂起,
    Ⅳ· 然后服务端的onTransact方法会被调用直到RPC过程返回后,当前线程继续执行,并从_reply中取出RPC过程的返回结果,最后返回_reply中的数据。

之所以提供AIDL文件,是为了方便系统为我们生成代码,我们完全可以自己实现Binder,AIDL的本质,是系统为我们提供了一种快速生成Binder的工具。

注意以下两点:

  • 当客户端发起远程请求的时候,当前的线程会被挂起,直到服务器端的远程进程返回结果,所以如果一个远程方法比较耗时的话,那么不能在UI线程中发起此远程请求。
  • 由于服务器端的Binder方法运行在服务器端的Binder线程池中,所以Binder方法不管是否耗时,都应该采用同步的方式实现,因为它有已经运行在一个线程中了。

给Binder设置一个死亡代理

Binder运行在服务器端的进程,当服务器端的进程由于某些原因异常终止,我们到服务器端的Binder链接断裂,这个时候如果我们不知道连接断开,那么由于远程调用失败,服务器端的功能将会受到影响,我们可以通过给Binder设置死亡代理来解决这个问题。步骤如下:

  1. 声明一个DeathRecipient对象。DeathRecipient只有一个方法binderDied,当Binder死亡的时候,系统就会回调DeathRecipient方法,这个时候我们就可以重新发起连接请求从而恢复连接。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient(){
    @Override
    public void binderDied(){
    if(mBookManager == null){
    return;
    }
    mBookManager.asBinder().unlinkToDeath(mDeathRecipient,0);
    mBookManager = null;
    // TODO:接下来重新绑定远程Service
    }
    }
  2. Binder有两个很重要的方法linkToDeath和unlinkToDeath。通过linkToDeath为Binder设置一个死亡代理。

    1
    2
    mService = IBookManager.Stub.asInterface(binder);
    binder.linkToDeath(mDeathRecipient,0);

另外,可以通过Binder的isBinderAlive判断Binder是否死亡。

总结自:《Android开发艺术探索》

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器