浅析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对象(此时唤醒线程)。这样就完成了一次进程间的通信。

JathonW
1 声望2 粉丝

精致唯一


« 上一篇
浅析Binder机制
下一篇 »
浅析HTTPS