Android 跨进程通信 Binder 工作流程

本篇仅讲述 Binder工作流程, 并不讲解Binder的原理

  1. 先说明什么是AIDL
    AIDL 是 android 接口定义语言,全称 Android Interface definition language
  2. AIDL和Binder作用
    AIDL是基于Binder机制的,使用AIDL可以让SDK自动帮我们生成对应的Binder类。
    Binder是不同进程之间通信的桥梁。
  3. 使用AIDL获取Binder实现类
  • 使用Android Studio 创建一个 AIDL 文件
    new->AIDL
  • 实例名称为IBookManager.aidl
    在AIDL中自定义的类型 需要使用import引入,并且自定义的类需要实现序列化接口(Serlizable或Parcelable),并使用单独的同名aidl文件声明,例如下例中 使用到的Book
package com.example.ipctest.aidl;
import com.example.ipctest.aidl.Book;
import com.example.ipctest.aidl.IOnNewBookArrivedListener;
// Declare any non-default types here with import statements

interface IBookManager {
    List<Book> getBookList(); //获取书籍
    void addBook(in Book book); //添加书籍
}

//Book.aidl
package com.example.ipctest.aidl;

// Declare any non-default types here with import statements

parcelable Book;

//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;
    }

    protected Book(Parcel in) {
        bookId = in.readInt();
        bookName = in.readString();
    }

    public static final Creator<Book> CREATOR = new Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(bookId);
        dest.writeString(bookName);
    }
}
  • build一下项目 就可以在build->generated->aidl_source_output_dir->debug目录下找到自动生成的IBookManager接口
    这个接口主要组成部分
  • 一个默认实现的静态类 Default
  • 一个静态抽象类Stub,继承Binder,实现本接口,这个就是Binder,同时它内部有一个静态内部类Proxy。可以把Stub看作服务端,Proxy看作是客户端。
  • 之前在AIDL中声明的方法,以及方法对应的code值
//所有可以在binder中传输的接口都需要继承IInterface
public interface IBookManager extends android.os.IInterface
{
  /** IBookManager接口的一个默认实现类 */
  public static class Default implements IBookManager
  {
    @Override public java.util.List<Book> getBookList() throws android.os.RemoteException
    {
      return null;
    }
    @Override public void addBook(Book book) throws android.os.RemoteException
    {
    }
    @Override
    public android.os.IBinder asBinder() {
      return null;
    }
  }
    
  //这相当于是一个binder,继承自Binder类 实现本接口
  public static abstract class Stub extends android.os.Binder implements IBookManager
  {
    //binder的唯一标识符  一般用当前binder的类名标识
    private static final String DESCRIPTOR = "com.example.ipctest.aidl.IBookManager";
    // 默认构造函数,将Stub附加到接口上 
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    /**
    静态方法
     * 将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,这种转换过程是区分进程的,如果客户端和服务端属于同一个进程
     * ,那么此方法返回的就是服务端Stub对象本身,否则返回的是系统封装后的Stub.proxy对象
     * 获取转换后的对象 就可以执行需要的方法
     */
    public static IBookManager asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      //查询实现了这个Binder对象的本地实例,如果是null就说明客户端和服务端不属于同一个进程,就需要去创建一个proxy对象返回给客户端
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof IBookManager))) {
        return ((IBookManager)iin);
      }
      //客户端和服务端不在一个进程 使用Proxy对Binder进行包装并返回,Proxy也实现了IBookManager接口
      return new IBookManager.Stub.Proxy(obj);
    }
    //返回当前binder对象
    @Override public android.os.IBinder asBinder()
    {
      return this;
    }
    //这个方法运行在Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理
    //调用远程服务的方法时,先执行Proxy中对应的方法,通过transact()去执行继承了Stub的类的对象的onTransact()方法,在onTransact方法中根据code值 去回调相应的接口,此时整个方法执行流程大致结束了
    @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
    {
      String descriptor = DESCRIPTOR;
      switch (code)
      {
        case INTERFACE_TRANSACTION:
        {
          reply.writeString(descriptor);
          return true;
        }
        case TRANSACTION_basicTypes:
        {
          data.enforceInterface(descriptor);
          int _arg0;
          _arg0 = data.readInt();
          long _arg1;
          _arg1 = data.readLong();
          boolean _arg2;
          _arg2 = (0!=data.readInt());
          float _arg3;
          _arg3 = data.readFloat();
          double _arg4;
          _arg4 = data.readDouble();
          String _arg5;
          _arg5 = data.readString();
          this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
          reply.writeNoException();
          return true;
        }
        case TRANSACTION_getBookList: 
        {
          data.enforceInterface(descriptor);
          //执行在服务端实现的getBookList()方法
          java.util.List<Book> _result = this.getBookList();
          reply.writeNoException();
          reply.writeTypedList(_result);
          return true;
        }
        case TRANSACTION_addBook:
        {
          data.enforceInterface(descriptor);
          Book _arg0;
          if ((0!=data.readInt())) {
            _arg0 = Book.CREATOR.createFromParcel(data);
          }
          else {
            _arg0 = null;
          }
            //执行在服务端实现的addBook()方法
          this.addBook(_arg0);
          reply.writeNoException();
          return true;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
    }
    //Stub的静态内部类 Proxy 相当于客户端
    private static class Proxy implements IBookManager
    {
      // 存放binder的引用,用于调用服务端的方法
      private android.os.IBinder mRemote;
      Proxy(android.os.IBinder remote)
      {
        mRemote = remote;
      }
      @Override public android.os.IBinder asBinder()
      {
        return mRemote;
      }
      public String getInterfaceDescriptor()
      {
        return DESCRIPTOR;
      }
      //getBookList()的实现
      @Override public java.util.List<Book> getBookList() throws android.os.RemoteException
      {
         //data是要传递的参数,reply存放返回值 result 存放结果
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        java.util.List<Book> _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          //先走跨进程通信的操作 看能否成功
          boolean _status = mRemote.transact(IBookManager.Stub.TRANSACTION_getBookList, _data, _reply, 0);
          //跨进程获取失败  说明 客户端和服务端在一个进程中 直接调用stub的getbooklist方法
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().getBookList();
          }
          _reply.readException();
          _result = _reply.createTypedArrayList(Book.CREATOR);
        }
        finally { //回收资源
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      @Override public void addBook(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);
          }
          boolean _status = mRemote.transact(IBookManager.Stub.TRANSACTION_addBook, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().addBook(book);
            return;
          }
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      public static IBookManager sDefaultImpl;
    }
    //声明两个整形id用于标识自定义的方法
    static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
    public static boolean setDefaultImpl(IBookManager impl) {
      // Only one user of this interface can use this function
      // at a time. This is a heuristic to detect if two different
      // users in the same process use this function.
      if (Proxy.sDefaultImpl != null) {
        throw new IllegalStateException("setDefaultImpl() called twice");
      }
      if (impl != null) {
        Proxy.sDefaultImpl = impl;
        return true;
      }
      return false;
    }
    public static IBookManager getDefaultImpl() {
      return Proxy.sDefaultImpl;
    }
  }
  //IBookManager接口中声明的方法
  public java.util.List<Book> getBookList() throws android.os.RemoteException;
  public void addBook(Book book) throws android.os.RemoteException;
}
  1. 工作流程
  1. 客户端(一个Activity)通过bindService()拿到服务端(一个Service组件)传递过来的Binder对象,并且调用IBookManager.Stub.asInterface()方法转化成客户端需要的IBookManager类型的对象(如果不在同一个进程,则使用Proxy类对Binder进行了包装并返回该对象)。通过这个对象可以调用服务端的方法
  2. 服务端 需要实现onbind方法,返回binder对象。这个binder对象所属的类必须继承IBookManager.Stub类,并实现IBookManager中声明的方法。(因为Stub是一个实现了IBookManager接口的抽象类,所以子类必须实现IBookManager接口声明的方法。)
  3. 在客户端调用服务端的一个方法,例如getBookList(),调用此方法后如果client和server不在同一个进程中就立刻调用Proxy中的相应的方法getBookList(),然后在该方法中,通过调用binder的transact()方法,转去执行Stub中的onTransaction()方法,在此方法中会调用你在服务端实现的getBookList()方法,然后依次返回结果,执行结束

注意:

  • binder中的方法都是在binder线程池中执行的,所以不要在服务端的实现接口方法中又再次开启线程,除非你确定需要那样做。
  • 远程服务的方法如果是耗时方法,则在客户端调用时需要开启线程,否则会ANR