从AIDL调用看Binder使用方法

前言:

最近在看VirtualApk的源码,发现里面很多涉及到代理模式

Binder调用本身就是使用一个超级复杂的代理模式

而我们接触到最多的Binder调用就是AIDL

因此这里分析一下AIDL的调用原理

Tips:此文章不讲述Binder底层的原理,只是通过AIDL调用的分析,来借花献佛

AIDL

Android Interface Definition Language

Android 接口定义语言

是android提供的一种本地调用远程服务接口的方式

先来看一下如何使用AIDL

首先定义AIDL接口

package vivo.testaidl;

interface IAidlInterface {

       int addition(int a,int b);

}

编译一下

服务端代码编写

public class MyAidlService extends Service {

    /**

     * 创建生成的本地 Binder 对象,实现 AIDL 制定的方法

     */

    private IBinder mIBinder = new IAidlInterface.Stub() {

        @Override

        public int addition(a,b) throws RemoteException {

            return a + b;

        }

    };

    /**

     * 客户端与服务端绑定时的回调,返回 mIBinder 后客户端就可以通过它远程调用服务端的方法,即实现了通讯

     * @param intent

     * @return

     */

    @Nullable

    @Override

    public IBinder onBind(Intent intent) {

        return mIBinder;

    }

}

客户端编写

private IAidlInterface mAidl;

private ServiceConnection mConnection = new ServiceConnection() {

    @Override

    public void onServiceConnected(ComponentName name, IBinder service) {

        //连接后拿到 Binder,转换成 AIDL,在不同进程会返回个代理

        mAidl = IAidlInterface.Stub.asInterface(service);

        int result = addition.

    }

    @Override

    public void onServiceDisconnected(ComponentName name) {

        mAidl = null;

    }

};

Intent intent1 = new Intent(getApplicationContext(), MyAidlService.class);

bindService(intent1, mConnection, BIND_AUTO_CREATE);

看上去很简单,AIDL对内部的一些处理做了封装,那么要了解AIDL运作的整个过程,我们不妨看一下AS根据AIDL文件生成的java源码

这个java源码就在generatedJava文件夹下

如图所示

生成的源码格式化后如下

/*

 * This file is auto-generated.  DO NOT MODIFY.

 * Original file: C:\\WorkTool\\vivo_workspace\\Collections\\aidl\\src\\main\\aidl\\vivo\\testaidl\\IAidlInterface.aidl

 */

package vivo.testaidl;

public interface IAidlInterface extends android.os.IInterface {

    /**

     * Local-side IPC implementation stub class.

     */

    public static abstract class Stub extends android.os.Binder implements vivo.testaidl.IAidlInterface {

        private static final java.lang.String DESCRIPTOR = "vivo.testaidl.IAidlInterface";

        /**

         * Construct the stub at attach it to the interface.

         */

        public Stub() {

            this.attachInterface(this, DESCRIPTOR);

        }

        /**

         * Cast an IBinder object into an vivo.testaidl.IAidlInterface interface,

         * generating a proxy if needed.

         */

        public static vivo.testaidl.IAidlInterface asInterface(android.os.IBinder obj) {

            if ((obj == null)) {

                return null;

            }

            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);

            if (((iin != null) && (iin instanceof vivo.testaidl.IAidlInterface))) {

                return ((vivo.testaidl.IAidlInterface) iin);

            }

            return new vivo.testaidl.IAidlInterface.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 {

            switch (code) {

                case INTERFACE_TRANSACTION: {

                    reply.writeString(DESCRIPTOR);

                    return true;

                }

                case TRANSACTION_addition: {

                    data.enforceInterface(DESCRIPTOR);

                    int _arg0;

                    _arg0 = data.readInt();

                    int _arg1;

                    _arg1 = data.readInt();

                    int _result = this.addition(_arg0, _arg1);

                    reply.writeNoException();

                    reply.writeInt(_result);

                    return true;

                }

            }

            return super.onTransact(code, data, reply, flags);

        }

        private static class Proxy implements vivo.testaidl.IAidlInterface {

            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 addition(int a, int b) 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(a);

                    _data.writeInt(b);

                    mRemote.transact(Stub.TRANSACTION_addition, _data, _reply, 0);

                    _reply.readException();

                    _result = _reply.readInt();

                finally {

                    _reply.recycle();

                    _data.recycle();

                }

                return _result;

            }

        }

        static final int TRANSACTION_addition = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

    }

    public int addition(int a, int b) throws android.os.RemoteException;

}

这一个个内部类继承又嵌套的看上去好难受,先不说google为啥这么写,拆分开再说

generatedJava中Stub继承自Binder 又实现了IAidlInterface接口,为了分离Stub和IAidlInterface 我们将其拆开,并在Stub初始化的时候传入一个实现了addition方法的IAidlInterface对象

asInterface()因为是一个静态公共方法,其实可以看成是一个工具方法,我们将其放在独立的Utils类下

因此我们将其拆分成4个类

1:AidlInterfaceBinderStub.java

下面请看源码注释

package vivo.testaidl;

/**

 * Created by chenlong on 2019/1/31.

 */

import android.util.Log;

/**

 * Stub 的几个关键内容介绍:

 * 构造函数

 * 调用了 attachInterface() 方法

 * 将一个描述符、特定的 IAidlInterface  与当前 Binder 绑定起来,这样后续调用 queryLocalInterface 就可以拿到这个

 * 这里使用一个 DESCRIPTOR,一般是类的具体路径名,来做key ,用于唯一表示这个 IAidlInterface

 * onTransact()

 * Binder 关键的处理事物方法

 * 根据传入的 code,调用本地/服务端的不同方法

 * AIDL是一个典型的远程代理模式

 */

public class AidlInterfaceBinderStub extends android.os.Binder {

    /**

     * 我们知道在跨进程通信中Binder对象是携带着IAidlInterface所定义的功能的

     * 但是Binder通信机制中那么多Binder都有Interface。那么系统怎么识别哪个Binder是哪个Binder呢?

     * 所以IAidlInterface只是一个能力的抽象,DESCRIPTOR就是来表示具体是哪一个功能Interface。

     */

    public static final String DESCRIPTOR = "vivo.testaidl.IAidlInterface";

    public static final String TAG = "TAG_IAidlInterface";

    private IAidlInterface mAidlInterface = null;

    /**

     * Construct the stub at attach it to the interface.

     * 在Binder内部保存mAidlInterface作为自己的owner

     * 和这个功能更对应的唯一描述descriptor,方便在通信的时候寻找。

     */

    public AidlInterfaceBinderStub(IAidlInterface mAidlInterface) {

        Log.d(AidlInterfaceBinderStub.TAG, "AidlInterfaceBinderStub---init  AidlInterfaceBinderStub = " this " mAidlInterface = " + mAidlInterface);

        this.mAidlInterface = mAidlInterface;

        this.attachInterface(mAidlInterface, DESCRIPTOR);

    }

    @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: {

                Log.d(AidlInterfaceBinderStub.TAG, "AidlInterfaceBinderStub---onTransact---   INTERFACE_TRANSACTION---" this);

                reply.writeString(DESCRIPTOR);

                return true;

            }

            case TRANSACTION_addition: {

                Log.d(AidlInterfaceBinderStub.TAG, "AidlInterfaceBinderStub---onTransact---   TRANSACTION_addition----" this);

                data.enforceInterface(DESCRIPTOR);

                int _arg0;

                _arg0 = data.readInt();

                int _arg1;

                _arg1 = data.readInt();

                // 调用 mAidlInterface的addition 方法,这个方法的实现是在服务端是 mAidlInterface服务端new出来的

                int _result = mAidlInterface.addition(_arg0, _arg1);

                // 得出方法返回值之后写写入序列化数据

                reply.writeNoException();

                reply.writeInt(_result);

                return true;

            }

        }

        return super.onTransact(code, data, reply, flags);

    }

    static final int TRANSACTION_addition = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

}

2 : AidlInterfaceProxy.java

package vivo.testaidl;

import android.util.Log;

/**

 * Created by chenlong on 2019/1/31.

 * AidlInterfaceProxy是一个使用代理模式实现的interface代理,当通讯双方不在一个进程时,IAidlInterface的方法传入的参数是需要做序列化的,此代理类就是代理实现序列化,并在完成序列化后之后

 * 调用传入的远端Binder的transact方法,远端binder的transact方法在执行之后,就会被代理执行到AidlInterfaceBinderStub中的onTransact实现参数的反序列化,方法执行,写入序列化结果,并最终在AidlInterfaceProxy的addition中返回

 */

public class AidlInterfaceProxy implements IAidlInterface {

    /**

     * 这里的mRemote其实就是其他进程传过来的BinderProxy

     * 代理的远端 IBinder

     */

    private android.os.IBinder mRemote;

    AidlInterfaceProxy(android.os.IBinder remote) {

        Log.d(AidlInterfaceBinderStub.TAG,"MyAidlInterfaceProxy---init");

        mRemote = remote;

    }

    @Override

    public android.os.IBinder asBinder() {

        return mRemote;

    }

    public String getInterfaceDescriptor() {

        return AidlInterfaceBinderStub.DESCRIPTOR;

    }

    @Override

    public int addition(int a, int b) throws android.os.RemoteException {

        Log.d(AidlInterfaceBinderStub.TAG,"MyAidlInterfaceProxy---addition");

        android.os.Parcel _data = android.os.Parcel.obtain();

        android.os.Parcel _reply = android.os.Parcel.obtain();

        int _result;

        try {

            _data.writeInterfaceToken(AidlInterfaceBinderStub.DESCRIPTOR);

            _data.writeInt(a);

            _data.writeInt(b);

            // 调用BinderProxy的transact方法

            // BinderProxy是通过transactNative来与远程Binder跨进程通信的

            mRemote.transact(AidlInterfaceBinderStub.TRANSACTION_addition, _data, _reply, 0);

            // 这里最终完成了远程调用 返回数据已经被序列化写入 , 下面就是读出来就行了

            _reply.readException();

            _result = _reply.readInt();

        finally {

            _reply.recycle();

            _data.recycle();

        }

        return _result;

    }

}

3 : AidlInterfaceUtils.java

package vivo.testaidl;

import android.util.Log;

/**

 * Created by chenlong on 2019/1/31.

 */

public class AidlInterfaceUtils {

    /**

     * Cast an IBinder object into an vivo.testaidl.IMyAidlInterface interface,

     * generating a proxy if needed.

     * 接收一个IBinder(这个IBinder是系统传入的), 把这个IBinder转化为它所具有功能接口

     * asInterface方法的作用是识别当前client与service是否在同一个进程,参数obj其实是onServiceConnected时传入的远端IBinder

     * 这里要注意一下 分两种情况

     * 1:当client和service在同一进程下时,onServiceConnected传入的binder就是service中onBind传入的binder对象,此时

     * asInterface在进行检测后(queryLocalInterface)会直接返回这个binder中的IMyAidlInterface对象,并调用IMyAidlInterface中定义的方法,此为本地调用,不经过ipc机制,效率高

     * 2:当client和service不在同一进程下时,onServiceConnected传入的binder是一个代理binder名叫BinderProxy

     * asInterface进行检测此BinderProxy(queryLocalInterface)并不包含IMyAidlInterface对象,于是返回一个IMyAidlInterface的代理对象MyAidlInterfaceBinderStub

     * 当client调用代理对象MyAidlInterfaceProxy的addition方法时会addition方法传入的参数进行序列化操作,然后调用BinderProxy的transact方法,

     * transact方法经过底层后会调用service中onBind传入的binder对象(MyAidlInterfaceStub)的onTransact,onTransact中实现序列化和反序列化,并最终调用MyAidlInterfaceStub

     * 中IMyAidlInterface的addition方法,MyAidlInterfaceBinderStub中的IMyAidlInterface是在service中实现的

     */

    public static IAidlInterface asInterface(android.os.IBinder obj) {

        Log.d(AidlInterfaceBinderStub.TAG, "AidlInterfaceUtils---asInterface---" obj.getClass().getName());

        if ((obj == null)) {

            return null;

        }

        //即如果参数descriptor和这个Binder的功能唯一描述相同。就会返回Binder的功能mOwner。

        //这里是同进程下的通讯queryLocalInterface会返回owner

        android.os.IInterface iin = obj.queryLocalInterface(AidlInterfaceBinderStub.DESCRIPTOR);

        if (((iin != null) && (iin instanceof IAidlInterface))) {

            return ((IAidlInterface) iin);

        }

        //调用者和Binder对象不在同一个进程 这时系统实际传的是一个BinderProxy

        //你可以理解为它是另一个进程中的Binder的替身。我们就可以把它当成另一个进程的Binder。

        // BinderProxy的queryLocalInterface()方法返回的是null

        //不同进程下返回的是一个Proxy

        return new AidlInterfaceProxy(obj);

    }

}

4 : IAidlInterface.java

/*

 * This file is auto-generated.  DO NOT MODIFY.

 * Original file: D:\\WorkTool\\vivo_workspace_demo\\TestAIDL\\app\\src\\main\\aidl\\vivo\\testaidl\\IMyAidlInterface.aidl

 */

package vivo.testaidl;

public interface IAidlInterface extends android.os.IInterface {

    int addition(int a, int b) throws android.os.RemoteException;

}

以上四个类就是全部AIDL的逻辑流程,是不是发现其实以后不需要写AIDL文件,只要实现以上四个类就可以完成一个自定义的binder远程调用了。

下面再画个图总结下

AIDL调用

在各方面都体现着代理模式的思想,了解并清楚了这个流程,对分析系统AMS有很大的帮助

上述内容有一部分来自于其他大神的文章摘抄,本用于自己学习记录所用,由于之前写的比较仓促没有记下网址,此处先表示感谢,如有朋友看到类似的的文段,实属必然,后面我会做好记录。

附code

AIDL.zip

猜你喜欢

转载自blog.csdn.net/binghelonglong123/article/details/87427919