AIDL基础

AIDL

什么是 AIDL?

AIDL 是 Android Interface definition language 的缩写,它是一种 Android 内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口。

为什么要设计出这么一门语言?

设计这门语言的目的是为了实现进程间通信,尤其是在涉及多进程并发情况下的进程间通信。

每一个进程都有自己的 VM 实例,都有自己的一块独立的内存,都在自己的内存上存储自己的数据,执行着自己的操作,都在自己的那片狭小的空间里过完自己的一生。

进程间互相隔离,内存隔离,而 AIDL,就是两个进程之间沟通的桥梁;通过 AIDL 来制定一些规则,规定它们能进行哪些交流——比如,它们可以在我们制定的规则下传输一些特定规格的数据。

它有哪些语法?

基本上它的语法和 Java 是一样的。

文件类型

用 AIDL 书写的文件的后缀是 .aidl,而不是 .java。

数据类型

AIDL 默认支持一些数据类型,在使用这些数据类型的时候是不需要导包的,但是除了这些类型之外的数据类型,在使用之前必须导包,就算目标文件与当前正在编写的 .aidl 文件在同一个包下(在 Java 中,这种情况是不需要导包的)。

默认支持的数据类型包括:

定向 tag

AIDL 中的定向 tag 表示了在跨进程通信中数据的流向。其中,数据流向是针对在客户端中的那个传入方法的对象而言的。

服务端到客户端数据流向不是返回值,而是传入参数对象服务端修改后会同步给客户端

Java 中的基本类型和 String ,CharSequence 的定向 tag 默认且只能是 in 。还有,请注意,请不要滥用定向 tag ,而是要根据需要选取合适的。

AIDL 中的定向 tag 表示了在跨进程通信中数据的流向,其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。其中,数据流向是针对在客户端中的那个传入方法的对象而言的。in 为定向 tag 的话表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动;out 的话表现为服务端将会接收到那个对象的的空对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;inout 为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。

案例:

interface BookManager {
    //保证客户端与服务端是连接上的且数据传输正常
    List<Book> getBooks();

    //通过三种定位tag做对比试验,观察输出的结果
    Book addBookIn(in Book book);
    Book addBookOut(out Book book);
    Book addBookInout(inout Book book);
}

测试:

public void addBookIn(View view) {
    //如果与服务端的连接处于未连接状态,则尝试连接
    if (!mBound) {
        attemptToBindService();
        Toast.makeText(this, "当前与服务端处于未连接状态,正在尝试重连,请稍后再试", Toast.LENGTH_SHORT).show();
        return;
    }
    if (mBookManager == null) return;

    Book book = new Book();
    book.setName("APP研发录In");
    book.setPrice(30);
    try {
        //获得服务端执行方法的返回值,并打印输出
        Book returnBook = mBookManager.addBookIn(book);
        Log.e("hacket", "addBookIn returnBook: " + returnBook.toString()
                + "-->> book:" + book.toString());
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}

public void addBookOut(View view) {
    if (!mBound) {
        attemptToBindService();
        Toast.makeText(this, "当前与服务端处于未连接状态,正在尝试重连,请稍后再试", Toast.LENGTH_SHORT).show();
        return;
    }
    if (mBookManager == null) return;

    Book book = new Book();
    book.setName("APP研发录Out");
    book.setPrice(30);
    try {
        Book returnBook = mBookManager.addBookOut(book);
        Log.e("hacket", "addBookOut returnBook: " + returnBook.toString() + "-->> book:" + book.toString());
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}

public void addBookInOut(View view) {
    if (!mBound) {
        attemptToBindService();
        Toast.makeText(this, "当前与服务端处于未连接状态,正在尝试重连,请稍后再试", Toast.LENGTH_SHORT).show();
        return;
    }
    if (mBookManager == null) return;

    Book book = new Book();
    book.setName("APP研发录InOut");
    book.setPrice(30);
    try {
        Book returnBook = mBookManager.addBookInout(book);
        Log.e("hacket", "addBookInOut returnBook: " + returnBook.toString() + "-->> book:" + book.toString());
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}

结果:

E/hacket: invoking addBooks() method , now the list is : [name : Android开发艺术探索 , price : 28, name : APP研发录In , price : 2333]
E/hacket: addBookIn returnBook: name : APP研发录In , price : 2333-->> book:name : APP研发录In , price : 30

E/hacket: invoking addBooks() method , now the list is : [name : Android开发艺术探索 , price : 28, name : APP研发录In , price : 2333, name : null , price : 2333]
E/hacket: addBookOut returnBook: name : null , price : 2333-->> book:name : null , price : 2333

E/hacket: invoking addBooks() method , now the list is : [name : Android开发艺术探索 , price : 28, name : APP研发录In , price : 2333, name : null , price : 2333, name : APP研发录InOut , price : 2333]
E/hacket: addBookInOut returnBook: name : APP研发录InOut , price : 2333-->> book:name : APP研发录InOut , price : 2333

原理:

@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
    switch (code) {
        // ...
        case TRANSACTION_addBookIn: {
            data.enforceInterface(DESCRIPTOR);
            //很容易看出来,_arg0就是输入的book对象
            com.lypeer.ipcclient.Book _arg0;
            //从输入的_data流中读取book数据,并将其赋值给_arg0
            if ((0 != data.readInt())) {
                _arg0 = com.lypeer.ipcclient.Book.CREATOR.createFromParcel(data);
            } else {
                _arg0 = null;
            }
            //在这里才是真正的开始执行实际的逻辑,调用服务端写好的实现
            this.addBookIn(_arg0);
            //执行完方法之后就结束了,没有针对_reply流的操作,所以客户端不会同步服务端的变化
            reply.writeNoException();
            return true;
        }
        case TRANSACTION_addBookOut: {
            data.enforceInterface(DESCRIPTOR);
            com.lypeer.ipcclient.Book _arg0;
            //可以看到,用out作为定向tag的方法里,根本没有从_data里读取book对象的操作,
            //而是直接new了一个book对象,这就是为什么服务端收不到客户端传过来的数据
            _arg0 = new com.lypeer.ipcclient.Book();
            //执行具体的事物逻辑
            this.addBookOut(_arg0);
            reply.writeNoException();
            //在这里,_arg0是方法的传入参数,故服务端的实现里对传参做出的任何修改,
            //都会在_arg0中有所体现,将其写入_reply流,就有了将这些修改传回客户端的前提
            if ((_arg0 != null)) {
                reply.writeInt(1);
                _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
            } else {
                reply.writeInt(0);
            }
            return true;
        }
        case TRANSACTION_addBookInout: {
            data.enforceInterface(DESCRIPTOR);
            com.lypeer.ipcclient.Book _arg0;
            //inout同样兼具上两个方法中的细节
            if ((0 != data.readInt())) {
                _arg0 = com.lypeer.ipcclient.Book.CREATOR.createFromParcel(data);
            } else {
                _arg0 = null;
            }
            this.addBookInout(_arg0);
            reply.writeNoException();
            if ((_arg0 != null)) {
                reply.writeInt(1);
                _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
            } else {
                reply.writeInt(0);
            }
            return true;
        }
    }
    return super.onTransact(code, data, reply, flags);
}

private static class Proxy implements com.lypeer.ipcclient.BookManager {
    private android.os.IBinder mRemote;

    // ...
    //通过三种定位tag做对比试验,观察输出的结果
    @Override
    public void addBookIn(com.lypeer.ipcclient.Book book) throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            //可以看到,这里执行的操作很简单,仅仅是判断book是否为空,
            // 如果为空,则_data写入int值1,将其book写入_data中
            // 如果不为空,则_data写入int值0
            if ((book != null)) {
                _data.writeInt(1);
                book.writeToParcel(_data, 0);
            } else {
                _data.writeInt(0);
            }
            //之后直接调用transact()方法,将方法的编码,
            // _data(包含从客户端流向服务端的book流),
            // _reply(包含从服务端流向客户端的数据流)传入
            mRemote.transact(Stub.TRANSACTION_addBookIn, _data, _reply, 0);
            _reply.readException();
        } finally {
            _reply.recycle();
            _data.recycle();
        }
    }

    @Override
    public void addBookOut(com.lypeer.ipcclient.Book book) throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            //在定向tag为out的方法里,没有将book对象写入_data流的操作
            mRemote.transact(Stub.TRANSACTION_addBookOut, _data, _reply, 0);
            _reply.readException();
            //与tag为in的方法里面不同的是,在执行transact方法之后,
            //还有针对_reply的操作,并且将book赋值为_reply流中的数据
            if ((0 != _reply.readInt())) {
                book.readFromParcel(_reply);
            }
        } finally {
            _reply.recycle();
            _data.recycle();
        }
    }

    @Override
    public void addBookInout(com.lypeer.ipcclient.Book book) throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            //定向tag为inout的方法里综合了上两个方法里的操作
            if ((book != null)) {
                _data.writeInt(1);
                book.writeToParcel(_data, 0);
            } else {
                _data.writeInt(0);
            }
            mRemote.transact(Stub.TRANSACTION_addBookInout, _data, _reply, 0);
            _reply.readException();
            if ((0 != _reply.readInt())) {
                book.readFromParcel(_reply);
            }
        } finally {
            _reply.recycle();
            _data.recycle();
        }
    }
}

两种 AIDL 文件

  1. 一类是用来定义 parcelable 对象,以供其他 AIDL 文件使用 AIDL 中非默认支持的数据类型的。
  2. 一类是用来定义方法接口,以供系统使用来完成跨进程通信的

所有的非默认支持数据类型必须通过第一类 AIDL 文件定义才能被使用

第一类 AIDL:

// Book.aidl
//第一类AIDL文件的例子
//这个文件的作用是引入了一个序列化对象 Book 供其他的AIDL文件使用
//注意:Book.aidl与Book.java的包名应当是一样的
package me.hacket.ipcclient;

//注意parcelable是小写
parcelable Book;

第二类 AIDL:

// BookManager.aidl
//第二类AIDL文件的例子
package me.hacket.ipcclient;
//导入所需要使用的非默认支持数据类型的包
import me.hacket.ipcclient.Book;

interface BookManager {

    //所有的返回值前都不需要加任何东西,不管是什么数据类型
    List<Book> getBooks();
    Book getBook();
    int getBookCount();

    //传参时除了Java基本类型以及String,CharSequence之外的类型
    //都需要在前面加上定向tag,具体加什么量需而定
    void setBookPrice(in Book book , int price)
    void setBookName(in Book book , String name)
    void addBookIn(in Book book);
    void addBookOut(out Book book);
    void addBookInout(inout Book book);
}

在进行跨进程通信的时候,在 AIDL 中定义的方法里包含非默认支持的数据类型与否,我们要进行的操作是不一样的。如果不包含,那么我们只需要编写一个 AIDL 文件,如果包含,那么我们通常需要写 n+1 个 AIDL 文件( n 为非默认支持的数据类型的种类数)。

我们应该如何使用它?

使数据类实现 Parcelable 接口

由于不同的进程有着不同的内存区域,并且它们只能访问自己的那一块内存区域,所以我们不能像平时那样,传一个句柄过去就完事了——句柄指向的是一个内存区域,现在目标进程根本不能访问源进程的内存,那把它传过去又有什么用呢?**所以我们必须将要传输的数据转化为能够在内存之间流通的形式。**这个转化的过程就叫做序列化与反序列化。简单来说是这样的:比如现在我们要将一个对象的数据从客户端传到服务端去,我们就可以在客户端对这个对象进行序列化的操作,将其中包含的数据转化为序列化流,然后将这个序列化流传输到服务端的内存中去,再在服务端对这个数据流进行反序列化的操作,从而还原其中包含的数据——通过这种方式,我们就达到了在一个进程中访问另一个进程的数据的目的。

AIDL 进行跨进程通信的时候,选择的序列化方式是 Parcelable。

若 AIDL 文件中涉及到的所有数据类型均为默认支持的数据类型,不需要什么操作,因为默认支持的那些数据类型都是可序列化的;如果有自定义的类型,需要引入。

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();
    }
}

AIDL 文件

// Book.aidl
//第一类AIDL文件
//这个文件的作用是引入了一个序列化对象 Book 供其他的AIDL文件使用
//注意:Book.aidl与Book.java的包名应当是一样的
package me.hacket.ipcclient;

//注意parcelable是小写
parcelable Book;
// BookManager.aidl
//第二类AIDL文件
//作用是定义方法接口
package me.hacket.ipcclient;
//导入所需要使用的非默认支持数据类型的包
import me.hacket.ipcclient.Book;

interface BookManager {

    //所有的返回值前都不需要加任何东西,不管是什么数据类型
    List<Book> getBooks();

    //传参时除了Java基本类型以及String,CharSequence之外的类型
    //都需要在前面加上定向tag,具体加什么量需而定
    void addBook(in Book book);
}

示例

计算器

ICalcAIDL. aidl

// ICalcAIDL.aidl
package me.hacket.assistant;
// Declare any non-default types here with import statements
interface ICalcAIDL {
    int add(int x, int y);
    int minus(int x, int y);
}

生成的. java 文件

package me.hacket.assistant;
// Declare any non-default types here with import statements

public interface ICalcAIDL extends android.os.IInterface
{
  /** Default implementation for ICalcAIDL. */
  public static class Default implements me.hacket.assistant.ICalcAIDL
  {
    @Override public int add(int x, int y) throws android.os.RemoteException
    {
      return 0;
    }
    @Override public int minus(int x, int y) throws android.os.RemoteException
    {
      return 0;
    }
    @Override
    public android.os.IBinder asBinder() {
      return null;
    }
  }
  /** Local-side IPC implementation stub class. */
  public static abstract class Stub extends android.os.Binder implements me.hacket.assistant.ICalcAIDL
  {
    private static final java.lang.String DESCRIPTOR = "me.hacket.assistant.ICalcAIDL";
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an me.hacket.assistant.ICalcAIDL interface,
     * generating a proxy if needed.
     */
    public static me.hacket.assistant.ICalcAIDL asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof me.hacket.assistant.ICalcAIDL))) {
        return ((me.hacket.assistant.ICalcAIDL)iin);
      }
      return new me.hacket.assistant.ICalcAIDL.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
    {
      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;
        }
        case TRANSACTION_minus:
        {
          data.enforceInterface(descriptor);
          int _arg0;
          _arg0 = data.readInt();
          int _arg1;
          _arg1 = data.readInt();
          int _result = this.minus(_arg0, _arg1);
          reply.writeNoException();
          reply.writeInt(_result);
          return true;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
    }
    private static class Proxy implements me.hacket.assistant.ICalcAIDL
    {
      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 int add(int x, int y) 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);
          _data.writeInt(x);
          _data.writeInt(y);
          boolean _status = mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().add(x, y);
          }
          _reply.readException();
          _result = _reply.readInt();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      @Override public int minus(int x, int y) 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);
          _data.writeInt(x);
          _data.writeInt(y);
          boolean _status = mRemote.transact(Stub.TRANSACTION_minus, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().minus(x, y);
          }
          _reply.readException();
          _result = _reply.readInt();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      public static me.hacket.assistant.ICalcAIDL sDefaultImpl;
    }
    static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_minus = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    public static boolean setDefaultImpl(me.hacket.assistant.ICalcAIDL impl) {
      if (Stub.Proxy.sDefaultImpl == null && impl != null) {
        Stub.Proxy.sDefaultImpl = impl;
        return true;
      }
      return false;
    }
    public static me.hacket.assistant.ICalcAIDL getDefaultImpl() {
      return Stub.Proxy.sDefaultImpl;
    }
  }
  public int add(int x, int y) throws android.os.RemoteException;
  public int minus(int x, int y) throws android.os.RemoteException;
}

如何通过它来达到我们的目的的?

AIDL 文件是怎么工作的?

AIDL 文件的目的其实就是为了生成用于跨进程通信的. java 文件,我们手动写这个文件也可以。

原理

从客户端获取 ICalcAIDL 入口来看:

class MyConn : ServiceConnection {
    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        ICalcAIDL mICalcAIDL = ICalcAIDL.Stub.asInterface(service)
    }
}

调用的是 ICalcAIDL.Stub.asInterface (service),而这个 service 调试发现就是个 BinderProxy

image.png

返回的为一个 Proxy 对象:

image.png

接着看 Stub. asInterface

public static me.hacket.assistant.ICalcAIDL asInterface(android.os.IBinder obj) {
    if ((obj==null)) {
        return null;
    }
    // 搜索本地是否已经有可用的对象了,如果有就将其返回
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin!=null)&&(iin instanceof me.hacket.assistant.ICalcAIDL))) {
        return ((me.hacket.assistant.ICalcAIDL)iin);
    }
    // 如果本地没有的话就新建一个返回
    return new me.hacket.assistant.ICalcAIDL.Stub.Proxy(obj);
}
private static class Proxy implements me.hacket.assistant.ICalcAIDL 
{
  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 int add(int x, int y) throws android.os.RemoteException
  {
    // ...
  }
  @Override public int minus(int x, int y) throws android.os.RemoteException
  {
    // _data用来存储流向服务端的数据流
    android.os.Parcel _data = android.os.Parcel.obtain();
    // _reply用来存储服务端流回客户端的数据流
    android.os.Parcel _reply = android.os.Parcel.obtain();
    int _result;
    try {
      _data.writeInterfaceToken(DESCRIPTOR);
      _data.writeInt(x);
      _data.writeInt(y);
      // 调用 transact()方法将方法id和两个 Parcel 容器传过去
      boolean _status = mRemote.transact(Stub.TRANSACTION_minus, _data, _reply, 0);
      if (!_status && getDefaultImpl() != null) {
        return getDefaultImpl().minus(x, y);
      }
      _reply.readException();
      // 从_reply中取出服务端执行方法的结果
      _result = _reply.readInt();
    }
    finally {
      _reply.recycle();
      _data.recycle();
    }
    // 将结果返回
    return _result;
  }
  static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
  static final int TRANSACTION_minus = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
  public static boolean setDefaultImpl(me.hacket.assistant.ICalcAIDL impl) {
      if (Stub.Proxy.sDefaultImpl == null && impl != null) {
        Stub.Proxy.sDefaultImpl = impl;
        return true;
      }
      return false;
  }
  public static me.hacket.assistant.ICalcAIDL getDefaultImpl() {
    return Stub.Proxy.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_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;
    }
    case TRANSACTION_minus:
    {
      data.enforceInterface(descriptor);
      int _arg 0;
      _arg 0 = data.readInt ();
      int _arg 1;
      _arg 1 = data.readInt ();
      int _result = this.minus (_arg 0, _arg 1);
      reply.writeNoException ();
      reply.writeInt (_result);
      return true;
    }
    default:
    {
      return super.onTransact (code, data, reply, flags);
    }
  }
}

为什么要这么设计这门语言?会不会有更好的方式来实现我们的目的?

Ref

AIDL in out inout oneway

in out inout

  1. in 参数使得实参顺利传到服务方,但服务方对实参的任何改变,不会反应给调用方
  2. out 参数使得实参不会真正传递到服务方,只是传一个实参的初始值过去,但服务方对实参的任何改变,在调用结束后会反应回调用方
  3. inout 是上面二者的结合,实参会顺利传到服务方,且服务方对实参的任何改变,在调用结束后会反应回调用方

其实 inout,都是相对于服务方,in 参数使得实参传到了服务方,所以是 in 进入了服务方,out 参数使得实参在调用结束后从服务方传回给调用方,所以 out 是从服务方出来

oneway

oneway 主要有两个特性:异步调用串行化处理

非 oneway 情况:
非 oneway 的话,Client 会挂起,相当于 Thread 的 sleep,底层调用的是 wait_event_interruptible() Linux 系统函数。
zrz96
oneway 情况:
oneway 的话,Client 就不需要挂起线程等待
pkeay

开源

remoter

GitHub - josesamuel/remoter: Remoter - An alternative to Android AIDL for Android Remote IPC services using plain java interfaces