android 中 aidl和binder 跨进程通讯

以前写过一个service的远程快进程调用,https://blog.csdn.net/Deaht_Huimie/article/details/52693191, 这一篇文章介绍的比较粗浅。

如果是两个app之间通讯,则两个app中的aidl需要一样,包括包名,通过绑定service和 ServiceConnection 来实现通讯,用法和一个app内跨进程通讯一样,这方面网上也有许多介绍。我们使用android studio 时,在 src 目录下的 main 目录中,创建 aidl 文件夹,然后创建自己所需要的文件夹名字,创建 .aidl 文件,例如 创建一个 ServiceAidlInterface.aidl

package duan.com.notictiontest;

interface ServiceAidlInterface {
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
                double aDouble, String aString);

    int add(int num1,int num2);
}

.aidl 是接口格式,add() 方法是我们自定义的通讯方法,basicTypes()方法是默认生成的,为了告诉我们可以传递哪些基本类型。如果我们要传递对象的话,对象需要序列化,实现Parcelable 接口;写好后,gradle 一下,然后系统就会自动生成对应的 ServiceAidlInterface.java 文件,我们可以看一下

package duan.com.notictiontest;
// Declare any non-default types here with import statements

public interface ServiceAidlInterface extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements duan.com.notictiontest.ServiceAidlInterface
{
    private static final java.lang.String DESCRIPTOR = "duan.com.notictiontest.ServiceAidlInterface";
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
        this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an duan.com.notictiontest.ServiceAidlInterface interface,
     * generating a proxy if needed.
     */
    public static duan.com.notictiontest.ServiceAidlInterface asInterface(android.os.IBinder obj)
    {
        if ((obj==null)) {
            return null;
        }
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (((iin!=null)&&(iin instanceof duan.com.notictiontest.ServiceAidlInterface))) {
            return ((duan.com.notictiontest.ServiceAidlInterface)iin);
        }
        return new duan.com.notictiontest.ServiceAidlInterface.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_basicTypes:
            {
                data.enforceInterface(DESCRIPTOR);
                int _arg0;
                _arg0 = data.readInt();
                long _arg1;
                _arg1 = data.readLong();
                boolean _arg2;
                _arg2 = (0!=data.readInt());
                float _arg3;
                _arg3 = data.readFloat();
                double _arg4;
                _arg4 = data.readDouble();
                java.lang.String _arg5;
                _arg5 = data.readString();
                this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
                reply.writeNoException();
                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;
            }
        }
        return super.onTransact(code, data, reply, flags);
    }
    private static class Proxy implements duan.com.notictiontest.ServiceAidlInterface
    {
        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;
        }
        /**
         * Demonstrates some basic types that you can use as parameters
         * and return values in AIDL.
         */
        @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws
android.os.RemoteException
        {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                _data.writeInt(anInt);
                _data.writeLong(aLong);
                _data.writeInt(((aBoolean)?(1):(0)));
                _data.writeFloat(aFloat);
                _data.writeDouble(aDouble);
                _data.writeString(aString);
                mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
                _reply.readException();
            }
            finally {
                _reply.recycle();
                _data.recycle();
            }
        }
        @Override public int add(int num1, int num2) 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(num1);
                _data.writeInt(num2);
                mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
                _reply.readException();
                _result = _reply.readInt();
            }
            finally {
                _reply.recycle();
                _data.recycle();
            }
            return _result;
        }
    }
    static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
    public int add(int num1, int num2) throws android.os.RemoteException;
}

我们可以看到, ServiceAidlInterface 也是个接口,实现了 IInterface 接口,

public interface IInterface
{
    public IBinder asBinder();
}

Stub 是个内部类,如果我们把它提出来,则 ServiceAidlInterface 的代码为

public interface ServiceAidlInterface extends android.os.IInterface
{
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
    public int add(int num1, int num2) throws android.os.RemoteException;
}
aidl 是对 Binder 的封装,我们只要按照格式写,系统会自动帮我们实现其他的代码,我们直接使用就可以了,避免自己做大量的代码工作。看上述代码,ServiceAidlInterface 中默认的只有一个 add() 方法,此时经过系统的生成,自动实现了 IInterface ,并生成内部类 Stub,跨进程主要就是依靠 IInterface,按照一定的协议进行数据交互。看看 Stub 都是什么:Binder 机制中,需要 binder和IInterface,同时需要把具体的操作给抽象出去,Binder 机制只是个框架及协议。 继续往下看
Stub 是个抽象类,继承了 Binder ,实现了 ServiceAidlInterface 接口,

private static final java.lang.String DESCRIPTOR = "duan.com.notictiontest.ServiceAidlInterface";
public Stub()
{
    this.attachInterface(this, DESCRIPTOR);
}

DESCRIPTOR 这个属性,是由 aidl 的包名和类名组成的,算是一个唯一的标识; 在 Stub 的构造函数中, 会把自身当做 IInterface 参数,和 DESCRIPTOR 一块传给 attachInterface方法,我们看一下 Binder 中的这两个方法

    public void attachInterface(IInterface owner, String descriptor) {
        mOwner = owner;
        mDescriptor = descriptor;
    }
    
    public IInterface queryLocalInterface(String descriptor) {
        if (mDescriptor.equals(descriptor)) {
            return mOwner;
        }
        return null;
    }

这个很明显了,创建 Stub 也就是 Binder 时,会给它打印上一个唯一的标记,并把 IInterface 赋值给Binder中的一个成员变量,然后通过 queryLocalInterface() 方法来查询出 IInterface 是否是我们需要的对象

public static duan.com.notictiontest.ServiceAidlInterface asInterface(android.os.IBinder obj){
    if ((obj==null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin!=null)&&(iin instanceof duan.com.notictiontest.ServiceAidlInterface))) {
        return ((duan.com.notictiontest.ServiceAidlInterface)iin);
    }
    return new duan.com.notictiontest.ServiceAidlInterface.Stub.Proxy(obj);
}

这里的 asInterface 方法,就是我们客户端定义 ServiceConnection 中 onServiceConnected(ComponentName componentName, IBinder iBinder) 方法中获取 ServiceAidlInterface adil 的调用的方法 : aidl = ServiceAidlInterface.Stub.asInterface(iBinder); 我们看 asInterface() 中,通过 obj.queryLocalInterface(DESCRIPTOR) 来获取所需要的对象,如果是,则返回,否则会创建一个 Proxy 对象,这里明显是用到了代理模式。

    public android.os.IBinder asBinder()
    {
        return this;
    }
这个方法是重写了 IInterface 接口中的方法,返回的对象就是自己。
    
onTransact()方法暂且跳过去,先不分析,我们看看代理类 Proxy

private static class Proxy implements duan.com.notictiontest.ServiceAidlInterface
{
    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 void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
    {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    try {
        _data.writeInterfaceToken(DESCRIPTOR);
        _data.writeInt(anInt);
        _data.writeLong(aLong);
        _data.writeInt(((aBoolean)?(1):(0)));
        _data.writeFloat(aFloat);
        _data.writeDouble(aDouble);
        _data.writeString(aString);
        mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
        _reply.readException();
    } finally {
            _reply.recycle();
            _data.recycle();
        }
    }

    @Override
    public int add(int num1, int num2) 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(num1);
            _data.writeInt(num2);
            mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
            _reply.readException();
            _result = _reply.readInt();
        }
        finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }

    static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

}

Proxy 实现了 ServiceAidlInterface 接口,同时接收了上面 asInterface(android.os.IBinder obj) 中的 obj,Proxy中定义了 IBinder mRemote 来接收这个参数,我们发现,里面多了两个自定义的静态常量值 TRANSACTION_basicTypes 和 TRANSACTION_add,通过名字可以看出,是用 TRANSACTION_ 拼接上我们自定义接口中的方法名,合成的常量名,它们对应的值是在 IBinder.FIRST_CALL_TRANSACTION = 0x00000001 的基础上依次累加,这个也是作为方法的唯一识别码。我们主要看 add() 方法,一开始创建两个 Parcel 对象,一个是用来写入数据,传递给服务端,一个是用来接收服务端返回的数据,然后返回给调用的地方; _data 在写入数据前,会 _data.writeInterfaceToken(DESCRIPTOR);把唯一的标识写入进去,然后是
_data.writeInt(num1); _data.writeInt(num2); 写入数据, mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0); 开启了远程调用,获取数据,_reply.readException();是进行异常校验,是否异常,如果有,会抛出异常;_result = _reply.readInt(); 是读取获取的数据,然后及时释放资源了。可能会有疑惑,远程调用 mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0); 是怎么获取数据的? 我们继续分析,Binder 实现了 IBinder 接口,看看 Binder 中 transact() 方法

public final boolean transact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException {
        if (false) Log.v("Binder", "Transact: " + code + " to " + this);
        if (data != null) {
            data.setDataPosition(0);
        }
        boolean r = onTransact(code, data, reply, flags);
        if (reply != null) {
            reply.setDataPosition(0);
        }
        return r;
    }

发现,最终会调用 boolean r = onTransact(code, data, reply, flags); 方法,如果 r 为true,则数据通讯成功,否则失败;我们看看 onTransact() 方法是什么,Binder 中的方法有些代码,浙西都是底层交互的,可以不用看,我们重点关注生成的 ServiceAidlInterface 中的代码,我们发现 Proxy 中没有这个方法,因为它是个代理类,况且是 mRemote 调用了 transact() 方法,我们看看 Stub 中的方法

    public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException{
        switch (code){
            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;
            }
        }
        return super.onTransact(code, data, reply, flags);
    }
这是简化后的代码, mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0); 这个是调用的代码,那么 code 就是 Stub.TRANSACTION_add, data 就是 _data,reply 就是 reply, data.enforceInterface(DESCRIPTOR); 对应 _data.writeInterfaceToken(DESCRIPTOR); 这一步应该校验, int _arg0 = data.readInt();int _arg1 = data.readInt();这一步就是读出传入的数据,然后调用 int _result = this.add(_arg0, _arg1);获取到值,reply.writeInt(_result);把值给传进去,对应着 Proxy 中 _result = _reply.readInt();取出数据;这里的 int _result = this.add(_arg0, _arg1) 方法,add() 是抽象方法,需要服务端自定义对象继承 Stub 类,来实现这个方法,并把它传给Service 中的 onBinder().
 
服务端 :

 public class IRemoteService extends Service {
    public IRemoteService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return new ServiceAidlServe();
//        return iBinder;
    }

    private IBinder iBinder = new ServiceAidlInterface.Stub(){

        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
    
        }
    
        @Override
        public int add(int num1, int num2) throws RemoteException {
        Log.i("AIDL","收到了请求,參數1"+num1+"參數2"+num2);
            return num1+num2;
        }
    };

    static class ServiceAidlServe extends ServiceAidlInterface.Stub{

        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
    
        }
    
        @Override
        public int add(int num1, int num2) throws RemoteException {
            Log.i("AIDL","ServiceAidlServe收到了请求,參數1"+num1+"參數2"+num2);
            return num1+num2;
        }
    }

}


客户端:

Activity 中代码: 
    
    private ServiceAidlInterface aidl;
    private ServiceConnection conn = new ServiceConnection() {
        //连接上
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            //拿到远程服务
            aidl = ServiceAidlInterface.Stub.asInterface(iBinder);

        }
        //断开时
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            //回收
            aidl = null;
        }
    };
    
    private void bindServices() {
        //获取服务端
        Intent intent = new Intent();
        //5.0之后的改变
        intent.setComponent(new ComponentName("duan.com.notictiontest","duan.com.notictiontest.IRemoteService"));
        //绑定服务
        bindService(intent,conn, Context.BIND_AUTO_CREATE);
    }

    private void testSum() {
        int num1 = 13;
        int num2 = 25;
        try {
            //结果
            int res = aidl.add(num1,num2);
            mTvSum.setText("结果:"+res);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

aidl 是封装好的模版,方便我们使用;掌握了原理后,我们也可以不用系统的aidl,完全自定义来实现此功能,但一般不建议这么干。

对于Stub的asInterface(Binder obj), 它可以返回 Stub 或 Stub.Proxy,如果客户端和服务端在同一个进程下,那么asInterface()返回的是 Stub,否则返回 Stub.Proxy。Stub 和 Stub.Proxy 的区别:(1)如果在同一个进程下的话,那么asInterface()将返回服务端的 Stub 对象本身,此时不需要跨进称通信,那么直接调用 Stub 对象的接口就可以了,返回的实现类就是服务端的 Stub 实现;(2)如果是不同进程,asInterface()返回是 Stub.Proxy 对象,该对象持有着远程的 Binder 引用,所以如果调用 Stub.Proxy 的接口的话,它们都将是IPC调用,会通过调用 transact 方法去与服务端通信。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值