Android Binder通讯机制

一、概念

从IPC的角度来说,Binder是android中一种跨进程的通讯方式,Binder可以理解为一种虚拟的物理设备,它的设备驱动是dev/binder;
从Android FrameWork角度来说,Binder是ServiceManager连接各种Manager(ActivityManager、WindowManager,等)和相应ManagerService的桥梁;
从Android应用层来说,Binder是客户端和服务端通信的媒介,当bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端的服务和数据;

二、工作机制

通过一个AIDL示例来了解Binder的工作机制,新建Movie.java、Movie.aidl和IMovieMgr.aidl三个文件,代码如下:

// Movie.java
public class Movie implements Parcelable{
  public String name;

  public Movie(String name) {
    this.name = name;
  }

  @Override public void writeToParcel(Parcel dest, int flags) {
  }

  @Override public int describeContents() {
    return 0;
  }
  public static final Parcelable.Creator<Movie> CREATOR = new Parcelable.Creator<Movie>() {
    @Override public Movie createFromParcel(Parcel source) {
      return new Movie(source);
    }

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

  private Movie(Parcel in) {
    this.name = in.readString();
  }
}

// Movie.aidl
package com.rico.aidl;
parcelable Movie;

// IMovieMgr.aidl
package com.rico.aidl;

// Declare any non-default types here with import statements
import com.rico.aidl.Movie;
interface IMovieMgr {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
    List<Movie> getMovieList();
    void addMovie(in Movie movie);
}

编译后系统自动生成build/generated/source/../IMovieMgr.java文件,手动格式化代码,通过IMovieMgr.java来了解下Binder的工作机制,代码如下:

package com.rico.aidl;

public interface IMovieMgr extends android.os.IInterface {
  /** Local-side IPC implementation stub class. */
  public static abstract class Stub extends android.os.Binder implements com.rico.aidl.IMovieMgr {
    private static final java.lang.String DESCRIPTOR = "com.rico.aidl.IMovieMgr";

    /** Construct the stub at attach it to the interface. */
    public Stub() {
      this.attachInterface(this, DESCRIPTOR);
    }

    /**
     * Cast an IBinder object into an com.rico.aidl.IMovieMgr interface,
     * generating a proxy if needed.
     */
    public static com.rico.aidl.IMovieMgr asInterface(android.os.IBinder obj) {
      if ((obj == null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin != null) && (iin instanceof com.rico.aidl.IMovieMgr))) {
        return ((com.rico.aidl.IMovieMgr) iin);
      }
      return new com.rico.aidl.IMovieMgr.Stub.Proxy(obj);
    }

    @Override public android.os.IBinder asBinder() {
      return this;
    }

    @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_addMovie: {
          data.enforceInterface(DESCRIPTOR);
          com.rico.aidl.Movie _arg0;
          if ((0 != data.readInt())) {
            _arg0 = com.rico.aidl.Movie.CREATOR.createFromParcel(data);
          } else {
            _arg0 = null;
          }
          this.addMovie(_arg0);
          reply.writeNoException();
          return true;
        }
      }
      return super.onTransact(code, data, reply, flags);
    }

    private static class Proxy implements com.rico.aidl.IMovieMgr {
      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;
      }

      // 省略代码

      @Override public java.util.List<com.rico.aidl.Movie> getMovieList()
          throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        java.util.List<com.rico.aidl.Movie> _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          mRemote.transact(Stub.TRANSACTION_getMovieList, _data, _reply, 0);
          _reply.readException();
          _result = _reply.createTypedArrayList(com.rico.aidl.Movie.CREATOR);
        } finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }

      @Override public void addMovie(com.rico.aidl.Movie movie) 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 ((movie != null)) {
            _data.writeInt(1);
            movie.writeToParcel(_data, 0);
          } else {
            _data.writeInt(0);
          }
          mRemote.transact(Stub.TRANSACTION_addMovie, _data, _reply, 0);
          _reply.readException();
        } finally {
          _reply.recycle();
          _data.recycle();
        }
      }
    }

    static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_getMovieList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    static final int TRANSACTION_addMovie = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
  }

  // 省略代码

  public java.util.List<com.rico.aidl.Movie> getMovieList() throws android.os.RemoteException;

  public void addMovie(com.rico.aidl.Movie movie) throws android.os.RemoteException;
}

首先IMovieMgr中声明了两个方法getMovieList和addMovie(这两个方法是我们在IMovieMgr.aidl中定义的),同时还声明了两个整型的id标识这两个方法,通过id来确定在transact过程中客户端请求的到底是哪个方法。

接着,它声明了一个Stub内部类,这个Stub就是Binder类,当客户端和服务端位于同一个进程,方法不会走跨进程的transact过程,而当位于不同进程,方法需要走跨进程transact,这个逻辑由Proxy来完成,下面详细介绍Stub和Proxy这两个类中每个方法的含义。

  • asInterface(android.os.IBinder obj)
    将服务端Binder对象转换成客户端需要的aidl接口类型对象,当客户端与服务端同进程,则返回Stub对象,不同进程则返回Stub.proxy对象
  • asBinder
    返回当前Binder对象
  • onTransact(int code, Parcel data, Parcel reply, int flag)
    该方法运行在服务端binder线程池中,客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法处理,服务端通过code来确定客户端具体调用了哪个方法,从data中获取参数,并将执行结果写入reply中,如果此方法返回false,那么客户端请求会失败,因此利用这个特性可以做接口访问权限验证。

需要注意的是,当客户端发起远程接口调用,当前线程挂起,直至服务端返回数据,所以不能再UI线程中调用远程接口,如果远程方法很耗时,会导致ANR,其次服务端binder方法运行在binder线程池中,所以binder方法需要采用同步的方式实现;

Binder工作机制:

android 修改 binder传输大小限制 安卓binder通信_多进程