Binder简介

由于Binder在Android的信息传输中占有比较重要的作用,所以把对Binder的分析单独出一篇文章来记录一下。

  1. 什么是Binder

    Binder,翻译为粘合剂,在Android进程间通讯相关的知识中经常出现。一般来说对Binder的解释通常有以下几种:

    • Binder是Android中的一个类,实现了IBinder接口。
    • Binder是Android独有的一种跨进程通信方式
    • Binder是一种虚拟的物理设备,可以用来连通客户端与服务端

    借用大神 Carson_Ho的一张图来表示的话是下面这样的:

    结合上图,大家应该可以对Binder有了一个较为清晰的定义了。

  2. Binder的使用场景

    Binder主要用在Service,包括AIDL和Messenger,其中普通Service中的Binder不涉及进程间的通讯,所以较为简单。Messenger的底层实现是AIDL,所以分析AIDL中的Binder就能够帮助我们理解Binder的工作原理了。

Binder结构分析

  1. AIDL和Binder的关系

    写完AIDL文件之后,系统会在Build时生成一个继承IInterface接口的java文件。这个文件名和对应的AIDL文件名相同。在这个文件中,有一个内部类Stub,这个类就是Binder。

    所以可以认为AIDL是为了帮助系统生成对应的Binder文件。

  2. 生成Binder的AIDL文件

    接下来我们写一个AIDL文件,AIDL的文件内容在下面的代码区域,如果想了解AIDL的整个流程,可以参考这篇文章

    // 文件名是:Book.aidlpackage com.wscq.aidltest.aidl;//aidl中用到了实现了序列化的类Book,所以这里需要申明一下parcelable Book;
    // 文件名是:IBookManager.aidlpackage com.wscq.aidltest.aidl;import com.wscq.aidltest.aidl.Book;import com.wscq.aidltest.aidl.IOnNewBookArrivedListener;interface IBookManager {   List getBookList();   void addBook(in Book book);}

    然后在java目录下需要有一个实现了Parcelable接口的Book.java类

    public class Book implements Parcelable {   public int bookId;   public String bookName;   public Book(int bookId, String bookName) {       this.bookId = bookId;       this.bookName = bookName;   }//省略get、set、Parcelable和toString方法}    

    上述文件写完以后,在AndroidStudio中会立即生成对应的Binder文件,位置在工程目录下/build/generated/source/aidl/debug/中。具体的路径如图所示:

    如果没有可以build一下,报错的话需要再检查一遍aidl是否书写正确或者放置的路径是否正确。在AndroidStudio中,正确的目录结构如下:

  3. Binder的总体结构

    在获取到对应的Binder文件后,我们先来看一下Binder的整体结构:

    public interface IBookManager extends IInterface {//声明内部类Stub,这个就是一个Binder类。   public static abstract class Stub extends Binder implements IBookManager {    //Binder的唯一标识,一般用当前Binder的类名表示       private static final String DESCRIPTOR = "com.wscq.aidltest.aidl.IBookManager";       //...       //客户端的代理类Proxy       private static class Proxy implements IBookManager {           //...       }    //这两个整型的ID用于标识在跨进程调用中。客户端到底调用的是哪个方法       static final int TRANSACTION_getBookList = (FIRST_CALL_TRANSACTION + 0);       static final int TRANSACTION_addBook = (FIRST_CALL_TRANSACTION + 1);   }//声明两个方法,也就是IBookManager.aidl中的方法   public List getBookList() throws RemoteException;   public void addBook(Book book) throws RemoteException;}

    这个类继承了IInterface接口,同时这个类也是一个接口。这个接口申明了两个方法,也就是IBookManager.aidl中的方法。然后声明了一个内部类Stub。这个Stub就是一个Binder类。在Stub内部还有个代理类Proxy,在跨进程通讯中个,它会是客户端的代理方法。

  4. Binder内方法详细分析

    1. 首先看看Stub中的各个方法

      除去构造方法以外,Stub中的方法还有asBinder()asInterface()onTransact方法。在这三个方法之外,还有上文提到过的,两个静态ID,用来标识客户端调用的方法。

      其中asBinder()方法相当于一个get方法,用来返回当前的Binder对象,这个代码比较简单,我们略过。

      接下来我们看一下onTransact()方法,这个方法运行在服务端,会通过code来分发具体要执行的方法。方法参数中的各个值代表的意义可以看下面注释:

      /*** 运行在服务端中的Binder线程池中,当客户端发起跨进程请求时,* 远程请求会通过系统底层封装后交由此方法处理** @param code  通过此code可以确定客户端所请求的目标方法是什么* @param data  目标方法中所需要的参数* @param reply 目标方法执行完后,向reply中写入返回值(若有)* @param flags 启动方式,这里并没有使用* @return false 表示请求失败, true表示请求成功* @throws android.os.RemoteException*/@Overridepublic boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {  //根据code来分发,运行对应的方法  switch (code) {        //...          return true;      }  }  return super.onTransact(code, data, reply, flags);}

      然后我们看下asInterface()方法:

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

      这个方法会判断当前服务端和客户端是否处于同一进程中。如果处于同一个进程中,会返回同一个Stub对象本身,如果处于不同的进程会返回封装后的客户端代理类Stub.proxy。这个方法会在客户端调用,用来获取一个IBookManager对象。

    2. 然后看看客户端代理Stub.Proxy中的方法

      这里主要的方法就是addBookgetBookList方法,其实这两个方法还是有部分相似之处的,这里先分析getBookList方法

      public java.util.List getBookList() throws android.os.RemoteException {  //客户端的参数合计,也就是Stub中onTransact的data参数  android.os.Parcel _data = android.os.Parcel.obtain();  //输出型对象,也就是Stub中onTransaction的reply参数  android.os.Parcel _reply = android.os.Parcel.obtain();  //返回值对象  java.util.List _result;  try {      //写入参数到_data中      _data.writeInterfaceToken(DESCRIPTOR);      //发起远程请求,当前线程会暂时挂起      mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);      //获取服务端返回信息      _reply.readException();      //从_reply中读取信息,构造_result      _result = _reply.createTypedArrayList(com.wscq.aidltest.aidl.Book.CREATOR);  } finally {      _reply.recycle();      _data.recycle();  }  //最后返回_result  return _result;}

      概括的来说,当客户端调动此方法时,会创建服务端的参数_data和接受返回的_reply以及返回值对象_result,然后把参数信息写入_data中。这里开始调用transact方法发起远程请求,同时当前线程挂起,当Stub的onTransact执行完后,再次回到当前方法,从_reply中取值,构造_result,然后返回_result

      addBook方法的过程和上述几乎一致,大家可以在最后面的完整代码中对照查看。

    3. 最后看IBookManager中的方法

      除去上面的两个内部类以外,剩余的代码就只有两个抽象的接口了,也就是IBookManager.aidl中定义的接口方法。这两方法没啥说的,主要用来被Stub和Stub.Proxy来继承或实现的。

  5. 上述Binder的完整代码

    这里附上完整的Binder对象,方便大家进行一些整体的研究:

    public interface IBookManager extends android.os.IInterface {   /**    * Local-side IPC implementation stub class.    */   public static abstract class Stub extends android.os.Binder implements com.wscq.aidltest.aidl.IBookManager {       //Binder的唯一标识,一般用当前列名表示       private static final java.lang.String DESCRIPTOR = "com.wscq.aidltest.aidl.IBookManager";       /**        * Construct the stub at attach it to the interface.        */       public Stub() {           this.attachInterface(this, DESCRIPTOR);       }       /**        * 用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,这个转换过程是区分进程的,        * 如果客户单服务端处于同一进程,此方法返回服务端的Stub,否则返回的是系统封装后的Stub.proxy对象        *        * @param obj 服务端的Binder对象        * @return 客户端所需的AIDL接口对象        */       public static com.wscq.aidltest.aidl.IBookManager asInterface(android.os.IBinder obj) {           if ((obj == null)) {               return null;           }           android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);           if (((iin != null) && (iin instanceof com.wscq.aidltest.aidl.IBookManager))) {               return ((com.wscq.aidltest.aidl.IBookManager) iin);           }           return new com.wscq.aidltest.aidl.IBookManager.Stub.Proxy(obj);       }       /**        * 用于返回当前的Binder对象        *        * @return 当前binder对象        */       @Override       public android.os.IBinder asBinder() {           return this;       }       /**        * 运行在服务端中的Binder线程池中,当客户端发起跨进程请求时,        * 远程请求会通过系统底层封装后交由此方法处理        *        * @param code  通过此code可以确定客户端所请求的目标方法是什么        * @param data  包含目标方法所需要的参数        * @param reply 目标方法执行完后,回想reply中写入返回值(若有)        * @param flags        * @return false表示请求失败, true表示请求成功        * @throws android.os.RemoteException        */       @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_getBookList: {                   data.enforceInterface(DESCRIPTOR);                   java.util.List _result = this.getBookList();                   reply.writeNoException();                   reply.writeTypedList(_result);                   return true;               }               case TRANSACTION_addBook: {                   data.enforceInterface(DESCRIPTOR);                   com.wscq.aidltest.aidl.Book _arg0;                   if ((0 != data.readInt())) {                       _arg0 = com.wscq.aidltest.aidl.Book.CREATOR.createFromParcel(data);                   } else {                       _arg0 = null;                   }                   this.addBook(_arg0);                   reply.writeNoException();                   return true;               }           }           return super.onTransact(code, data, reply, flags);       }       private static class Proxy implements com.wscq.aidltest.aidl.IBookManager {           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;           }           /**            * @return 返回_reply中的数据            * @throws android.os.RemoteException            */           @Override           public java.util.List getBookList() throws android.os.RemoteException {               //客户端的参数合计,也就是Stub中onTransact的data参数               android.os.Parcel _data = android.os.Parcel.obtain();               //输出型对象,也就是Stub中onTransaction的reply参数               android.os.Parcel _reply = android.os.Parcel.obtain();               //返回值对象               java.util.List _result;               try {                   //写入参数到_data中                   _data.writeInterfaceToken(DESCRIPTOR);                   //发起远程请求,当前线程会暂时挂起                   mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);                   //获取服务端返回信息                   _reply.readException();                   //从_reply中读取信息                   _result = _reply.createTypedArrayList(com.wscq.aidltest.aidl.Book.CREATOR);               } finally {                   _reply.recycle();                   _data.recycle();               }               return _result;           }           /**            * 和上面的方法类似,不过由于没有返回值,所以不需要从_reply中取出返回值            *            * @param book 要添加的书籍信息            * @throws android.os.RemoteException            */           @Override           public void addBook(com.wscq.aidltest.aidl.Book book) throws android.os.RemoteException {               android.os.Parcel _data = android.os.Parcel.obtain();               android.os.Parcel _reply = android.os.Parcel.obtain();               try {                   _data.writeInterfaceToken(DESCRIPTOR);                   if ((book != null)) {                       _data.writeInt(1);                       book.writeToParcel(_data, 0);                   } else {                       _data.writeInt(0);                   }                   mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);                   _reply.readException();               } finally {                   _reply.recycle();                   _data.recycle();               }           }       }       static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);       static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);   }   public java.util.List getBookList() throws android.os.RemoteException;   public void addBook(com.wscq.aidltest.aidl.Book book) throws android.os.RemoteException;}

相关文章:

  • android中常见的IPC机制
  • Android IPC之AIDL详解
  • Android IPC之Messenger的使用
  • Socket通讯原理及举例

更多相关文章

  1. Android判断网络状态是否断开+Android完全关闭应用程序+ 本文讲
  2. 17.Android与JavaScript相互调用
  3. Android(安卓)Intent 教程
  4. android Instrumentation
  5. Android原始XML的读写操作
  6. 更新android studio gradle 不成功解决方法
  7. android之SQLite
  8. Android使用代码模拟HOME键的功能
  9. Android知识点

随机推荐

  1. android 6.0sd卡内部存储 & 外部存储
  2. Android开发之获取SHA1
  3. An internal error occurred during: "Ch
  4. android 问题汇总系列之八
  5. android persistent属性研究
  6. Android(安卓)ADT 默认的模拟器内存调整
  7. android定位
  8. android 圆形ListView实现,并附带圆角Imag
  9. 第一章:初入Android大门(Style 样式和But
  10. 解决android模拟器太大,小屏幕无法完全显