Android中的Binder详解

Binder简介

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

  1. 什么是Binder

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

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

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

    Android中的Binder详解_第1张图片

    结合上图,大家应该可以对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.aidl
    package com.wscq.aidltest.aidl;
    
    //aidl中用到了实现了序列化的类Book,所以这里需要申明一下
    parcelable Book;
    // 文件名是:IBookManager.aidl
    package 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/中。具体的路径如图所示:

    Android中的Binder详解_第2张图片

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

    Android中的Binder详解_第3张图片

  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
      */
      @Override
      public 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通讯原理及举例

你可能感兴趣的:(Android,IPC)