AIDL in out inout 的区别

2,427 阅读3分钟

AIDL in out inout

in out inout 区别

他们都用来修饰传入参数, 区别如下

  1. in: 对象中的变量可以从客户端传到服务端, 而服务端对该对象的修改不会影响到客户端.

  2. out: 对象中的变量无法从客户端传到服务端, 服务端收到的是一个内部变量全为初始值的变量, 但是服务端对该对象的的修改可以影响到客户端

  3. inout: 对象中的变量既可以传到服务端, 服务队对其的修改也可以影响到客户端

具体原理

一个可以通过 Binder 传递的对象

  1. 必须 要实现 Parcelable 接口. 即必须要重写 writeToParcel(Parcel, int), 重写了该方法, 说明该对象有 in 的能力了

  2. 可选 可以添加一个 readFromParcel(Parcel) 方法, 添加了该方法, 说明该对象有 out 的能力. 如果没有添加该方法, 却用 out 或者 inout 修饰了, 会报错.

IAidlListener.aidl 的定义: 很简单的一个方法 putIPCBean(inout IPCBean io, in IPCBean i, out IPCBean o);

上代码:

IAidlInterface.Stub.Proxy 客户端的 服务端代理类

      @Override public void putIPCBean(com.example.myapplication.bean.IPCBean inout, com.example.myapplication.bean.IPCBean in, com.example.myapplication.bean.IPCBean out) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);

          //region inout in, 会将数据写入到 Parcel, 也可以说明 out 不会将对象传递过去
          if ((inout!=null)) {
            _data.writeInt(1);
            inout.writeToParcel(_data, 0);
          }
          else {
            _data.writeInt(0);
          }
          if ((in!=null)) {
            _data.writeInt(1);
            in.writeToParcel(_data, 0);
          }
          else {
            _data.writeInt(0);
          }
          //endregion

          //IPC
          boolean _status = mRemote.transact(Stub.TRANSACTION_putIPCBean, _data, _reply, 0);

          _reply.readException();
          // 数据写回到对象

          //region inout, out 会调用 readFromParcel(Parcel) 方法来根据服务端返回值修改内部变量
          // 这就是没有添加该方法的类 如果用 inout, out 修饰会报错的原因
          if ((0!=_reply.readInt())) {
            inout.readFromParcel(_reply);
          }
          if ((0!=_reply.readInt())) {
            out.readFromParcel(_reply);
          }
          //endregion
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }

长求总

从客户端来看:

  1. out 修饰的对象不会调用 writeToParcel(Parcel, int), 即不会写入到 Parcel 中, 所以服务端不会收到该对象.

  2. inout, out 修饰的对象会调用 readFromParcel(Parcel), 即根据服务端的改动, 修改自己的内部变量. 这就是没有添加该方法的类 如果用 inout, out 修饰会报错的原因.

IAidlInterface.Stub 即服务端的实现

    // 变量名已经修改为对应数据的流向了
    @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
    {
      // ... 省略其他不相干的
      switch (code)
      {
        case TRANSACTION_putIPCBean:
        {
          data.enforceInterface(descriptor);

          //region inout, in, 可以看到都是从 客户端传过来的 Parcel 中生成对象的, 
          // 即客户端中对象的变量 可以传到服务端
          com.example.myapplication.bean.IPCBean inout;
          if ((0!=data.readInt())) {
            inout = com.example.myapplication.bean.IPCBean.CREATOR.createFromParcel(data);
          }
          else {
            inout = null;
          }
          com.example.myapplication.bean.IPCBean in;
          if ((0!=data.readInt())) {
            in = com.example.myapplication.bean.IPCBean.CREATOR.createFromParcel(data);
          }
          else {
            in = null;
          }
          //endregion

          //region out, 直接构造对象, 所以内部变量都是初始值
          com.example.myapplication.bean.IPCBean out;
          out = new com.example.myapplication.bean.IPCBean();
          //endregion

          this.putIPCBean(inout, in, out);

          // 数据写回到客户端
          reply.writeNoException();

          //region inout, out 传入的对象在服务端的改动, 会写回到 Parcel 中, 可以传递回客户端
          if ((inout!=null)) {
            reply.writeInt(1);
            inout.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
          }
          else {
            reply.writeInt(0);
          }
          if ((out!=null)) {
            reply.writeInt(1);
            out.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
          }
          else {
            reply.writeInt(0);
          }
          //endregion

          return true;
        }
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
    }

长求总

从服务端来看:

  1. 在构造对象时, 只会把被 in inout 修饰的对象构建出来. (因为用 out 修饰的对象 没有写入Parcel, 上文提到了).

  2. 在写入 reply 数据时, 只会调用被 inout out 修饰的对象的 writeToParcel(Parcel, int) 方法.