AIDL 关键字 in out inout oneway 解析

1,700 阅读5分钟

完整 Android Framework 教程:yuandaimaahao.github.io/AndroidFram…

视频课程与答疑服务请咨询微信号 zzh0838

关键字的作用

in out inout 是 aidl 中的 directional tag,表示了在跨进程通信中数据的流向:

  • in 表示数据只能由客户端流向服务端,服务端会获取到客户端完整的数据,但客户端不会同步服务端你对该对象的修改,不写的话,默认的 tag 就是 in
  • out 表示数据只能由服务端流向客户端,从服务端端接受该对象不为空,但字段内容为空,服务端修改对象后,binder 远程调用返回后,客户端会收到修改后的对象。
  • inout 则表示数据可在服务端与客户端之间双向流通。
  • 默认情况下,我们在 AIDL 中定义的接口方法是同步的,如果 AIDL 中的接口方法被 oneway 修饰了,那么这些方法就变成异步的了。

Java 中的 AIDL 关键字解析

修改Binder 程序示例之 Java 篇中的示例程序:

项目整体结构如下:

BinderJavaDemo
└── com
    └── yuandaima
        ├── Android.bp
        ├── Book.aidl
        ├── Book.java
        ├── Client.java
        ├── HelloService.java
        ├── IHelloService.aidl
        ├── IHelloService.java
        └── Server.java

其中 Book.java

package com.yuandaima;

import android.os.Parcel;
import android.os.Parcelable;

public class Book implements Parcelable{
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    private String name;
    private int price;
    public Book(){}

    public Book(Parcel in) {
        name = in.readString();
        price = in.readInt();
    }

    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.writeString(name);
        dest.writeInt(price);
    }

    /**
     * 参数是一个Parcel,用它来存储与传输数据
     * @param dest
     */
    public void readFromParcel(Parcel dest) {
        //注意,此处的读值顺序应当是和writeToParcel()方法中一致的
        name = dest.readString();
        price = dest.readInt();
    }

    //方便打印数据
    @Override
    public String toString() {
        return "name : " + name + " , price : " + price;
    }
}

Book.aidl 内容如下:

package com.yuandaima;

parcelable Book;

接着修改 IHelloService.aidl

package com.yuandaima;

import com.yuandaima.Book;

interface IHelloService
{
	int sayhelloin(in Book book);
	int sayhelloout(out Book book);
	int sayhelloinout(inout Book book);
	oneway void sayhellooneway(in Book book);
}

接下来在项目目录下执行 aidl 编译命令:

aidl -I . IHelloService.aidl

执行完该命令后生成对应的 IHelloService.java 源码文件,我们来看下内部的 Proxy 类的实现:

    private static class Proxy implements com.yuandaima.IHelloService
    {
      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;
      }

      //in 表示数据从客户端传递给服务端
      @Override public int sayhelloin(com.yuandaima.Book book) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        int _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          if ((book!=null)) {
            _data.writeInt(1);
            book.writeToParcel(_data, 0); //数据写入 Parcel 对象
          }
          else {
            _data.writeInt(0);
          }
          boolean _status = mRemote.transact(Stub.TRANSACTION_sayhelloin, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().sayhelloin(book);
          }
          _reply.readException();
          _result = _reply.readInt();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }

      //out 表示数据从服务端回传给客户端
      @Override public int sayhelloout(com.yuandaima.Book book) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        int _result;

        //发送前不会向 Parcel 写入数据
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          boolean _status = mRemote.transact(Stub.TRANSACTION_sayhelloout, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().sayhelloout(book);
          }
          _reply.readException();
          _result = _reply.readInt();
          if ((0!=_reply.readInt())) {
            book.readFromParcel(_reply); //从返回数据中读出 book
          }
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }

      // inout 表示数据从客户端传递给服务端,同时服务端也会传回客户端
      @Override public int sayhelloinout(com.yuandaima.Book book) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        int _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          if ((book!=null)) {
            _data.writeInt(1);
            book.writeToParcel(_data, 0); // 数据写入 Parcel 对象
          }
          else {
            _data.writeInt(0);
          }
          boolean _status = mRemote.transact(Stub.TRANSACTION_sayhelloinout, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().sayhelloinout(book);
          }
          _reply.readException();
          _result = _reply.readInt();
          if ((0!=_reply.readInt())) {
            book.readFromParcel(_reply);   //从返回数据中读出 book
          }
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      @Override public void sayhellooneway(com.yuandaima.Book book) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          if ((book!=null)) {
            _data.writeInt(1);
            book.writeToParcel(_data, 0);
          }
          else {
            _data.writeInt(0);
          }
          //发送数据时,最后一个参数是 FLAG_ONEWAY,表示调用是异步的
          boolean _status = mRemote.transact(Stub.TRANSACTION_sayhellooneway, _data, null, android.os.IBinder.FLAG_ONEWAY);
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().sayhellooneway(book);
            return;
          }
        }
        finally {
          _data.recycle();
        }
      }
      public static com.yuandaima.IHelloService sDefaultImpl;
    }

接着我们在看先服务端 onTransact 中是如何处理数据的:

    @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_sayhelloin:
        {
          data.enforceInterface(descriptor);
          com.yuandaima.Book _arg0;
          if ((0!=data.readInt())) {
            //从 Parcel 中读出数据
            _arg0 = com.yuandaima.Book.CREATOR.createFromParcel(data);
          }
          else {
            _arg0 = null;
          }
          int _result = this.sayhelloin(_arg0);
          reply.writeNoException();
          reply.writeInt(_result);
          return true;
        }
        case TRANSACTION_sayhelloout:
        {
          data.enforceInterface(descriptor);
          com.yuandaima.Book _arg0;
          _arg0 = new com.yuandaima.Book();
          int _result = this.sayhelloout(_arg0);
          reply.writeNoException();
          reply.writeInt(_result);
          if ((_arg0!=null)) {
            reply.writeInt(1);
            // 将数据写入需要返回的 Parcel 对象中
            _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
          }
          else {
            reply.writeInt(0);
          }
          return true;
        }
        case TRANSACTION_sayhelloinout:
        {
          data.enforceInterface(descriptor);
          com.yuandaima.Book _arg0;
          if ((0!=data.readInt())) {
            //读取数据
            _arg0 = com.yuandaima.Book.CREATOR.createFromParcel(data);
          }
          else {
            _arg0 = null;
          }
          int _result = this.sayhelloinout(_arg0);
          reply.writeNoException();
          reply.writeInt(_result);
          if ((_arg0!=null)) {
            reply.writeInt(1);
            //写入数据
            _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
          }
          else {
            reply.writeInt(0);
          }
          return true;
        }
        case TRANSACTION_sayhellooneway:
        {
          data.enforceInterface(descriptor);
          com.yuandaima.Book _arg0;
          if ((0!=data.readInt())) {
            _arg0 = com.yuandaima.Book.CREATOR.createFromParcel(data);
          }
          else {
            _arg0 = null;
          }
          this.sayhellooneway(_arg0);
          return true;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
    }

从源码我们可以看出,in out 的实现是通过是否从 Parcel 对象中读写数据实现的。oneway 是通过将 transact 方法最后一个参数设置为 FLAG_ONEWAY 实现的。

C++ 中的 AIDL 关键字解析

修改AIDL 数据类型详解之 C++ 篇中的示例程序:

修改项目中的 IHello.aidl:

package com.yuandaima;

import com.yuandaima.Student;

interface IHello
{
    void hello();
    int sum(int x, int y);
    // int printList(in List<String> strs);
	// int printMap(in Map maps);
    int printStudent(in Student student);
    int printStudent1(out Student student);
    int printStudent2(inout Student student);
    oneway void printStudent3(in Student student);
}

接着编译 aidl 文件:

aidl-cpp -I . com/yuandaima/IHello.aidl ./ ./IHello.cpp

接着我们看下生成的客户端代码:

::android::binder::Status BpHello::printStudent(const ::com::yuandaima::Student& student, int32_t* _aidl_return) {
  ::android::Parcel _aidl_data;
  ::android::Parcel _aidl_reply;
  ::android::status_t _aidl_ret_status = ::android::OK;
  ::android::binder::Status _aidl_status;
  _aidl_ret_status = _aidl_data.writeInterfaceToken(getInterfaceDescriptor());
  if (((_aidl_ret_status) != (::android::OK))) {
    goto _aidl_error;
  }

  //发送前向 Parcel 写入数据 student
  _aidl_ret_status = _aidl_data.writeParcelable(student);
  if (((_aidl_ret_status) != (::android::OK))) {
    goto _aidl_error;
  }
  _aidl_ret_status = remote()->transact(::android::IBinder::FIRST_CALL_TRANSACTION + 2 /* printStudent */, _aidl_data, &_aidl_reply);
  if (UNLIKELY(_aidl_ret_status == ::android::UNKNOWN_TRANSACTION && IHello::getDefaultImpl())) {
     return IHello::getDefaultImpl()->printStudent(student, _aidl_return);
  }
  if (((_aidl_ret_status) != (::android::OK))) {
    goto _aidl_error;
  }
  _aidl_ret_status = _aidl_status.readFromParcel(_aidl_reply);
  if (((_aidl_ret_status) != (::android::OK))) {
    goto _aidl_error;
  }
  if (!_aidl_status.isOk()) {
    return _aidl_status;
  }
  _aidl_ret_status = _aidl_reply.readInt32(_aidl_return);
  if (((_aidl_ret_status) != (::android::OK))) {
    goto _aidl_error;
  }
  _aidl_error:
  _aidl_status.setFromStatusT(_aidl_ret_status);
  return _aidl_status;
}

::android::binder::Status BpHello::printStudent1(::com::yuandaima::Student* student, int32_t* _aidl_return) {
  ::android::Parcel _aidl_data;
  ::android::Parcel _aidl_reply;
  ::android::status_t _aidl_ret_status = ::android::OK;
  ::android::binder::Status _aidl_status;
  _aidl_ret_status = _aidl_data.writeInterfaceToken(getInterfaceDescriptor());
  if (((_aidl_ret_status) != (::android::OK))) {
    goto _aidl_error;
  }
  //发送前,不会将 student 写入 Parcel
  _aidl_ret_status = remote()->transact(::android::IBinder::FIRST_CALL_TRANSACTION + 3 /* printStudent1 */, _aidl_data, &_aidl_reply);
  if (UNLIKELY(_aidl_ret_status == ::android::UNKNOWN_TRANSACTION && IHello::getDefaultImpl())) {
     return IHello::getDefaultImpl()->printStudent1(student, _aidl_return);
  }
  if (((_aidl_ret_status) != (::android::OK))) {
    goto _aidl_error;
  }
  _aidl_ret_status = _aidl_status.readFromParcel(_aidl_reply);
  if (((_aidl_ret_status) != (::android::OK))) {
    goto _aidl_error;
  }
  if (!_aidl_status.isOk()) {
    return _aidl_status;
  }
  _aidl_ret_status = _aidl_reply.readInt32(_aidl_return);
  if (((_aidl_ret_status) != (::android::OK))) {
    goto _aidl_error;
  }

  //从返回的数据中读出 student
  _aidl_ret_status = _aidl_reply.readParcelable(student);
  if (((_aidl_ret_status) != (::android::OK))) {
    goto _aidl_error;
  }
  _aidl_error:
  _aidl_status.setFromStatusT(_aidl_ret_status);
  return _aidl_status;
}


::android::binder::Status BpHello::printStudent2(::com::yuandaima::Student* student, int32_t* _aidl_return) {
  ::android::Parcel _aidl_data;
  ::android::Parcel _aidl_reply;
  ::android::status_t _aidl_ret_status = ::android::OK;
  ::android::binder::Status _aidl_status;
  _aidl_ret_status = _aidl_data.writeInterfaceToken(getInterfaceDescriptor());
  if (((_aidl_ret_status) != (::android::OK))) {
    goto _aidl_error;
  }

    //发送前向 Parcel 写入数据 student
  _aidl_ret_status = _aidl_data.writeParcelable(*student);
  if (((_aidl_ret_status) != (::android::OK))) {
    goto _aidl_error;
  }
  _aidl_ret_status = remote()->transact(::android::IBinder::FIRST_CALL_TRANSACTION + 4 /* printStudent2 */, _aidl_data, &_aidl_reply);
  if (UNLIKELY(_aidl_ret_status == ::android::UNKNOWN_TRANSACTION && IHello::getDefaultImpl())) {
     return IHello::getDefaultImpl()->printStudent2(student, _aidl_return);
  }
  if (((_aidl_ret_status) != (::android::OK))) {
    goto _aidl_error;
  }
  _aidl_ret_status = _aidl_status.readFromParcel(_aidl_reply);
  if (((_aidl_ret_status) != (::android::OK))) {
    goto _aidl_error;
  }
  if (!_aidl_status.isOk()) {
    return _aidl_status;
  }
  _aidl_ret_status = _aidl_reply.readInt32(_aidl_return);
  if (((_aidl_ret_status) != (::android::OK))) {
    goto _aidl_error;
  }

  //从返回的数据中读出 student
  _aidl_ret_status = _aidl_reply.readParcelable(student);
  if (((_aidl_ret_status) != (::android::OK))) {
    goto _aidl_error;
  }
  _aidl_error:
  _aidl_status.setFromStatusT(_aidl_ret_status);
  return _aidl_status;
}

::android::binder::Status BpHello::printStudent3(const ::com::yuandaima::Student& student) {
  ::android::Parcel _aidl_data;
  ::android::Parcel _aidl_reply;
  ::android::status_t _aidl_ret_status = ::android::OK;
  ::android::binder::Status _aidl_status;
  _aidl_ret_status = _aidl_data.writeInterfaceToken(getInterfaceDescriptor());
  if (((_aidl_ret_status) != (::android::OK))) {
    goto _aidl_error;
  }
  _aidl_ret_status = _aidl_data.writeParcelable(student);
  if (((_aidl_ret_status) != (::android::OK))) {
    goto _aidl_error;
  }

  //通过函数最后一个参数 FLAG_ONEWAY 指明远程调用是异步的
  _aidl_ret_status = remote()->transact(::android::IBinder::FIRST_CALL_TRANSACTION + 5 /* printStudent3 */, _aidl_data, &_aidl_reply, ::android::IBinder::FLAG_ONEWAY);
  if (UNLIKELY(_aidl_ret_status == ::android::UNKNOWN_TRANSACTION && IHello::getDefaultImpl())) {
     return IHello::getDefaultImpl()->printStudent3(student);
  }
  if (((_aidl_ret_status) != (::android::OK))) {
    goto _aidl_error;
  }
  _aidl_error:
  _aidl_status.setFromStatusT(_aidl_ret_status);
  return _aidl_status;
}

从源码中可以看出 native 代码和 Java 层是一样的:in out 的实现是通过是否从 Parcel 对象中读写数据实现的。oneway 是通过将 transact 函数最后一个参数设置为 FLAG_ONEWAY 实现的。

参考资料

关于

我叫阿豪,2015 年本科毕业于国防科学技术大学指挥信息系统专业,毕业后从事信息化装备的研发工作,主要研究方向是 Android Framework 与 Linux Kernel。

如果你对 Android Framework 感兴趣或者正在学习 Android Framework,可以关注我的微信公众号和抖音,我会持续分享我的学习经验,帮助正在学习的你少走一些弯路。学习过程中如果你有疑问或者你的经验想要分享给大家可以添加我的微信,我拉你进技术交流群。