深入解析AIDL

Posted by Allen Vork on May 6, 2018

aidl 调用过程

从上一篇文章中可以知道,客户端绑定服务后,会在 onServiceConnected() 拿到 IBinder,然后通过调用 IAvengerAidlInterface.Stub.asInterface(service) 将 IBinder 转换成IAvengerAidlInterface。然后通过它来调用 addAvenger 等方法。这个过程都是在编译 aidl 文件后生成的 java 文件中完成的。

生成的 java 文件的结构


上图为 java 文件的结构,里面有一个 Stub 类,Stub 中还有一个 Proxy 的内部类。我们通过 IAvengerAidlInterface.Stub.asInterface(service) 得到的实际是这个 Proxy 类的实例。通过它调用接口的方法时会调用 transact 方法,该方法会调用到服务器的 onTransact 方法,此时客户端的线程会被挂起,直到服务器端返回。所以如果服务器端是耗时操作,客户端需要在线程中执行方法的调用。而服务器端的 onTransact 方法是运行在 Binder 线程池中,所以不管方法耗不耗时都应该采用同步的方法实现。Stub 类中的 onTransact(int code, .Parcel data, Parcel reply, int flags) 方法是真正执行相关 aidl 接口的方法。执行完成后将结果装入一个 reply 的 Parcel 中返回给客户端。

详解该 java 文件的具体实现

public interface IAvengerAidlInterface extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements IAvengerAidlInterface {
        private static final String DESCRIPTOR = "com.example.aidlserver.IAvengerAidlInterface";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * 将 IBinder 转换成我们所创建的进程间通信的接口,或者创建一个 Proxy
         * @param obj
         * @return
         */
        public static IAvengerAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            //如果客户端和服务器端在同一个进程,则直接返回服务器端的Stub对象(即Service中onBind()中返回的IBinder
            if (((iin != null) && (iin instanceof IAvengerAidlInterface))) {
                return ((IAvengerAidlInterface) iin);
            }
            //如果不在同一进程,则返回系统封装后的Stub.Proxy,它是一个代理类,用它来实现跨进程通信
            return new Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        /**
         * 客户端调用服务器端的通信接口的具体实现
         * @param code 这个 code 对应服务器端所有执行的方法,譬如我们写了一个 getAvengers() 方法,那么就会对应生成一个 code 如 TRANSACTION_getAvengers 用于 switch-case。
         * @param data 方法里面的参数
         * @param reply 方法的返回值
         * @param flags false 代表客户端请求失败
         * @return
         * @throws android.os.RemoteException
         */
        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getAvengers: {
                    data.enforceInterface(DESCRIPTOR);
                    //调用服务器端 getAvengers() 方法,然后写到 reply 中返回给客户端
                    java.util.List<com.example.aidlserver.aidlmodel.Avenger> _result = this.getAvengers();
                    reply.writeNoException();
                    //数据的传递都是通过 Parcelable 实现的。这样你就知道为什么自定义的数据结构都要实现 parcelable 了吧
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addAvengerIn: {
                    data.enforceInterface(DESCRIPTOR);
                    com.example.aidlserver.aidlmodel.Avenger _arg0;
                    if ((0 != data.readInt())) {
                        //调用 Avenger.CREATOR.createFromParcel 用于创建一个 Avenger 对象,并将参数传进去
                        _arg0 = com.example.aidlserver.aidlmodel.Avenger.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    //通过 data 构造完方法所需要的参数后,开始调用服务器端的方法
                    this.addAvengerIn(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_addAvengerInout: { // inout 相当于结合了 in 和 out
                    data.enforceInterface(DESCRIPTOR);
                    com.example.aidlserver.aidlmodel.Avenger _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.example.aidlserver.aidlmodel.Avenger.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addAvengerInout(_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;
                }
                case TRANSACTION_addAvengerOut: {
                    data.enforceInterface(DESCRIPTOR);
                    com.example.aidlserver.aidlmodel.Avenger _arg0;
                    //out 直接 new 一个空对象,参数直接不要然后传给服务器。相当于没用传一个容器给服务器
                    _arg0 = new com.example.aidlserver.aidlmodel.Avenger();
                    this.addAvengerOut(_arg0); // 服务器端可以往这个“容器”填充内容
                    reply.writeNoException();
                    if ((_arg0 != null)) {
                        reply.writeInt(1);
                        //再将该(修改过的)“容器”的内容写到 reply ,它后面会被写回到客户端传进来的那个对象,如果它被服务器端修改
                        //那么客户端的该对象就会被同步修改。This is the trick that the out tag playes.
                        _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 IAvengerAidlInterface {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public java.util.List<com.example.aidlserver.aidlmodel.Avenger> getAvengers() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.example.aidlserver.aidlmodel.Avenger> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getAvengers, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.example.aidlserver.aidlmodel.Avenger.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            //in 为定向 tag 的话,服务端将会接收到该对象的完整数据,但是客户端的那个对象不会因为服务端对该参数的修改而发生变动
            @Override
            public void addAvengerIn(com.example.aidlserver.aidlmodel.Avenger avenger) throws android.os.RemoteException {
                //创建两个 Parcel,一个用于存放参数,一个用于存放返回值
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    //将客户端传过来的 avenger 对象中的参数都写到 data 中
                    if ((avenger != null)) {
                        _data.writeInt(1);
                        avenger.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    //调用 transact 后,该远程请求会经过系统底层封装后调用到服务器端的 onTransact,客户端的该线程会被挂起,
                    //那么如果该方法很耗时就要开线程调用了。而 onTransact 方法本身就是在服务器端的 binder 线程池中执行的,所以
                    //Binder方法不管耗不耗时都应该采用同步的方法实现。
                    //我们可以看到 transact 方法传的不是 Avenger ,而是该对象的 Parcel,这就解释了后面调用的 onTransact
                    //方法为什么要直接 New 或者采用 Parcelable 的 Create 来创建一个新 Avenger 了。
                    mRemote.transact(Stub.TRANSACTION_addAvengerIn, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            //inout 为定向 tag 时,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。
            @Override
            public void addAvengerInout(com.example.aidlserver.aidlmodel.Avenger avenger) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((avenger != null)) {
                        //先写个1进去代表 dada 中有值,这就解释了上面的 onTransact 方法中判断 data.getInt() != 0 才去创建 Avenger
                        _data.writeInt(1);
                        avenger.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addAvengerInout, _data, _reply, 0);
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        avenger.readFromParcel(_reply);
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            //out 的话,服务端将会接收到一个参数为空的对象,但服务端该“空”对象修改之后客户端会同步变动
            @Override
            public void addAvengerOut(com.example.aidlserver.aidlmodel.Avenger avenger) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    //从上面可知会调用到 onTransact 方法,由于参数的 tag 为 out。 那么这个
                    //_reply 就已经被服务器端赋值了
                    mRemote.transact(Stub.TRANSACTION_addAvengerOut, _data, _reply, 0); 
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        //将 _reply 的内容拷贝给 avenger,这样服务器如果对客户端传过去的 avenger
                        //修改了的话,客户端也会同步改变
                        avenger.readFromParcel(_reply);
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_getAvengers = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addAvengerIn = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_addAvengerInout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
        static final int TRANSACTION_addAvengerOut = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
    }

    public java.util.List<com.example.aidlserver.aidlmodel.Avenger> getAvengers() throws android.os.RemoteException;

    public void addAvengerIn(com.example.aidlserver.aidlmodel.Avenger avenger) throws android.os.RemoteException;

    public void addAvengerInout(com.example.aidlserver.aidlmodel.Avenger avenger) throws android.os.RemoteException;

    public void addAvengerOut(com.example.aidlserver.aidlmodel.Avenger avenger) throws android.os.RemoteException;
}

下面我们看下 Binder 工作机制图

结合上面的解释很好懂。