浅析AIDL

前言

AIDL(Android Interface Define Language) 是IPC过程间通信形式的一种.用于生成能够在Android设施上两个过程之间进行过程间通信(interprocess communication, IPC)的代码.通过简要剖析AIDL的应用,还能够让咱们进一步了解Binder的通信机制。

1 实例阐明

假如一个情景咱们须要计算a+b,咱们须要在客户端传递两个参数a和b,而后将参数传递给服务端(另一个过程)来进行计算,计算结果传递给客户端。

1.1 在我的项目中创立AIDL文件

命名为INumManager.aidl

interface INumManager {    int add(int num1,int num2);}

编译实现为后生成如下一个文件

public interface INumManager extends android.os.IInterface{  /** Default implementation for INumManager. */  // 默认实现  ...  /** Local-side IPC implementation stub class. */  // Stub类  public static abstract class Stub extends android.os.Binder implements com.jathow.listviewtest.INumManager  {    private static final java.lang.String DESCRIPTOR = "com.jathow.listviewtest.INumManager";    /** Construct the stub at attach it to the interface. */    public Stub()    {      this.attachInterface(this, DESCRIPTOR);    }    /**     * Cast an IBinder object into an com.jathow.listviewtest.INumManager interface,     * generating a proxy if needed.     */    //将服务端的Binder对象转换成客户须要的AIDL对象,转换辨别过程,客户端服务端位于同一过程,返回服务端的    //Stub对象自身;否则返回的是零碎的封装后的Stub.proxy对象。    public static com.jathow.listviewtest.INumManager asInterface(android.os.IBinder obj)    {      if ((obj==null)) {        return null;      }      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);      if (((iin!=null)&&(iin instanceof com.jathow.listviewtest.INumManager))) {        return ((com.jathow.listviewtest.INumManager)iin);      }      return new com.jathow.listviewtest.INumManager.Stub.Proxy(obj);    }    @Override public android.os.IBinder asBinder()    {      return this;    }        //运行在服务端的Binder线程池中    @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException    {      java.lang.String descriptor = DESCRIPTOR;      switch (code)      {        case INTERFACE_TRANSACTION:        {          reply.writeString(descriptor);          return true;        }        case TRANSACTION_add:        {          data.enforceInterface(descriptor);          int _arg0;          _arg0 = data.readInt();          int _arg1;          _arg1 = data.readInt();          int _result = this.add(_arg0, _arg1);          reply.writeNoException();          reply.writeInt(_result);          return true;        }        default:        {          return super.onTransact(code, data, reply, flags);        }      }    }        //代理类,运行在客户端    private static class Proxy implements com.jathow.listviewtest.INumManager    {      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;      }            //客户端调用此办法,传递进来num1和num2两个参数      @Override public int add(int num1, int num2) throws android.os.RemoteException      {        android.os.Parcel _data = android.os.Parcel.obtain();        android.os.Parcel _reply = android.os.Parcel.obtain();        int _result;        try {          //向_data中写入参数          _data.writeInterfaceToken(DESCRIPTOR);          _data.writeInt(num1);          _data.writeInt(num2);          //通过transact办法向服务端传递参数,并调用了办法,返回的后果写入_reply中          boolean _status = mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);          if (!_status && getDefaultImpl() != null) {            return getDefaultImpl().add(num1, num2);          }          _reply.readException();          _result = _reply.readInt();        }        finally {          _reply.recycle();          _data.recycle();        }        return _result;      }      public static com.jathow.listviewtest.INumManager sDefaultImpl;    }    //标识位    static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);    ... 获取默认实现  }  public int add(int num1, int num2) throws android.os.RemoteException;}

在这个编译文件中,次要蕴含了三个货色,别离是INumManager接口,接口的动态抽象类Stub,和Stub的动态外部类。
咱们一个个来剖析:

  • INumManager接口
    该接口继承了IInterface类,所有的AIDL接口都须要继承IInterface类,其次要的性能就是标记Server过程对象能提供哪里办法(AIDL文件中定义的接口)
  • Stub类(该类继承了Binder类,实现了INumManager接口)

    • Binder类实现了IBinder接口,IBinder接口代表了一种跨过程通信的能力,实现该接口的类都能够跨过程传输。阐明Stub是一个Binder的本地对象,其能够进行跨过程通信。
    • 实现INumManager接口,即表明具备 Server 承诺给 Client 的能力
    • asInterface办法,其常见应用是在ServiceConnection中,其会调用Stub.asInterface(Binder binder)拿到跨过程通信对象。从源码中咱们能够发现,该类会先进行一步查找obj.queryLocalInterface(DESCRIPTOR),判断Binder是不是本地对象(Client和Server在同一过程之中),如果是则间接应用会,如果不是则会调用Stub.Proxy(obj)生成一个代理对象。
    • onTransact办法,此办法运行在服务端中的Binder线程池中,当客户端发动跨过程申请时,近程申请会通过零碎底层封装后交由此办法解决。
  • Proxy类(实现了INumManager接口)

    • 在Stub类中,int add(int num1, int num2)是一个形象办法,Client端须要继承并实现它。

      • 如果Client和Server都在一个过程则间接实现即可
      • 如果是近程调用,Client调用Server的办法就须要通过Binder代理来实现,也就是下面的Proxy。
    • Proxy的办法中,先通过Parcel将数据序列化,向data中写入参数,而后通过transact办法向服务端传递参数,此时调用此办法的线程会挂起,而后服务端的onTransact办法被调用,依据相干的编号,调动相应的函数,之后将返回的后果写入_reply中,而后返回,唤醒挂起线程,取出数据。

1.2 创立一个Service来监听客户端连贯申请

public class IRemoteService extends Service {    //客户端绑定service时会执行    @Override    public IBinder onBind(Intent intent) {        return iBinder;    }    private IBinder iBinder = new IImoocAIDL.Stub(){        @Override        public int add(int num1, int num2) throws RemoteException {            Log.e("TAG","收到了来自客户端的申请" + num1 + "+" + num2 );            return num1 + num2;        }    };}

1.3 客户端绑定服务并调用服务端办法

public class MainActivity extends AppCompatActivity implements View.OnClickListener {    private EditText num1;    private EditText num2;    private Button button;    private TextView text;    private IImoocAIDL iImoocAIDL;    private ServiceConnection conn = new ServiceConnection() {        //绑定服务,回调onBind()办法        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            iImoocAIDL = IImoocAIDL.Stub.asInterface(service);        }        @Override        public void onServiceDisconnected(ComponentName name) {            iImoocAIDL = null;        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        bindService();        initView();    }    private void initView() {        num1 = (EditText) findViewById(R.id.num1);        num2 = (EditText) findViewById(R.id.num2);        button = (Button) findViewById(R.id.button);        text = (TextView) findViewById(R.id.text);        button.setOnClickListener(this);    }    @Override    public void onClick(View v) {        int num11 = Integer.parseInt(num1.getText().toString());        int num22 = Integer.parseInt(num2.getText().toString());        try {            int res = iImoocAIDL.add(num11,num22);            text.setText(num11 +"+"+ num22 +"="+ res);        } catch (RemoteException e) {            e.printStackTrace();        }    }    private void bindService() {        Intent intent = new Intent();        //绑定服务端的service        intent.setAction("com.mecury.aidltest.IRomoteService");        //新版本(5.0后)必须显式intent启动 绑定服务        intent.setComponent(new ComponentName("com.mecury.aidltest","com.mecury.aidltest.IRemoteService"));        //绑定的时候服务端主动创立        bindService(intent,conn, Context.BIND_AUTO_CREATE);    }    @Override    protected void onDestroy() {        super.onDestroy();        unbindService(conn);    }}

如此,就能够进行一次AIDL的调用。

2 AIDL调用流程

咱们当初来略微梳理一下整个IPC的流程

  • 首先,咱们在服务端,建设了一个AIDL的接口,该接口外部有Stub类(Binder对象)须要咱们后续去实现。
  • 而后,咱们在客户端利用这个Stub类去获取Binderd对象

    • 如果是同一个过程,则间接操作Binder类
    • 如果不是同一个过程,则取得代理对象Proxy
  • Proxy对象调用某个办法,会将参数存入Parcel,并调用transact办法(此时阻塞线程),Binder驱动承受到这个拜访之后,就会去查问本人的表单,寻找与代理对象绝对应的Binder对象,而后调用告诉服务器端调用Ontransact对象,并把返回后果发给驱动,之后再将这个后果放回给Proxy对象(此时唤醒线程)。这样就实现了一次过程间的通信。