欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android中的Binder详解

程序员文章站 2024-03-23 19:25:40
...

Binder简介

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

  1. 什么是Binder

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

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

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

    Android中的Binder详解

    结合上图,大家应该可以对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<Book> 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详解

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

    Android中的Binder详解

  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<Book> 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<com.wscq.aidltest.aidl.Book> 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<com.wscq.aidltest.aidl.Book> _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<com.wscq.aidltest.aidl.Book> _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<com.wscq.aidltest.aidl.Book> 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<com.wscq.aidltest.aidl.Book> _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<com.wscq.aidltest.aidl.Book> getBookList() throws android.os.RemoteException;
    
       public void addBook(com.wscq.aidltest.aidl.Book book) throws android.os.RemoteException;
    }

相关文章:

上一篇: C++自学笔记

下一篇: