Android进程间通信(IPC)之AIDL

AIDL:Android Interface Definition Language,即Android接口定义语言;使用AIDL文件可以帮我们生成一个java类(Binder接口类),这个类里面会含有Binder,所以通信的关键还是Binder;
对于大并发请求,我们通常使用AIDL来进行跨进程通信.
当然对于这个AIDL文件生成的类我们也可以手写,但是开发中很麻烦;但是私底下想要理解Binder深刻一点,自己可以多写几遍,我就是这样做的;
ADIL文件支持哪些数据类型
1.基本数据类型(除了short之外)
2.String和CharSequence
3.List:只支持ArrayList,里面每个元素都必须能够被AIDL支持
4.Map:只支持HashMap,里面每个元素都必须能够被AIDL支持,包括key和value
5.Parcelable:所有实现了Pracelable接口的对象
6.ADIL:所有AIDL接口本身也可以在AIDL文件中使用
书写AIDL文件的时候,需要注意一下几点:
1.其他AIDL自动生成的接口需要导入
2.其它实现android.os.Pracelable接口的类,需要导入
3.除了基本数据类型以外,所有的参数都要表上 方向in、out、inout;
4.实现了android.os.Pracelable接口的类,要想在aidl文件中使用,需要在相同包下新建一个同名aidl文件。
5.当服务端和客户端不在一个应用时,需要保持AIDL文件和类结构一致,最好全局放在一个包中,这样序列化和反序列化很方便,否则会报错。
6。AIDL接口中只支持方法,不支持静态常量。


比如我们写一个ICaltotalsum.aidl文件,里面有个方法专门计算两个数据的和;

package com.csdn.aidl;

interface ICaltotalsum {
    int sum(int x,int y);
}

这个AIDL文件会自动生成一个ICaltotalsum的java的接口类文件如下;

package com.csdn.aidl;

public interface ICaltotalsum extends android.os.IInterface {

    public static abstract class Stub extends android.os.Binder implements
            com.csdn.aidl.ICaltotalsum {
        private static final java.lang.String DESCRIPTOR = "com.csdn.aidl.ICaltotalsum";

        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        public static com.csdn.aidl.ICaltotalsum asInterface(
                android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.csdn.aidl.ICaltotalsum))) {
                return ((com.csdn.aidl.ICaltotalsum) iin);
            }
            return new com.csdn.aidl.ICaltotalsum.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_sum: {
                data.enforceInterface(DESCRIPTOR);
                int _arg0;
                _arg0 = data.readInt();
                int _arg1;
                _arg1 = data.readInt();
                int _result = this.sum(_arg0, _arg1);
                reply.writeNoException();
                reply.writeInt(_result);
                return true;
            }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.csdn.aidl.ICaltotalsum {
            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 sum(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);
                    mRemote.transact(Stub.TRANSACTION_sum, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

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

    public int sum(int x, int y) throws android.os.RemoteException;
}

1.首先分析一下这个java类,ICaltotalsum接口类要想在在Binder中跨进程传递必须要实现IInterface接口,至于为什么我也清楚,只是IInterface接口的注释上面是这么说的;不信自己去看;
2.其次Stub这个类public static abstract class Stub extends Binder implements ICaltotalsum
继承了Binder接口,并且本身也实现了AIDL接口类ICaltotalsum,这样Stub就成了一个Binder。
Stub的构造方法

        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

attachInterface()这个方法是在父类Binder中的方法,这里子类继承了Binder,所以相当于自己的方法;查看内部实现;

 public void attachInterface(IInterface owner, String descriptor) {
        mOwner = owner;
        mDescriptor = descriptor;
    }

当我们创建Stub类的时候,mOwner指向我们new的这个Stub对象,mDescriptor指向DESCRIPTOR;
private static final java.lang.String DESCRIPTOR = “com.csdn.aidl.ICaltotalsum”;就是这个字符串常量;
Stub中的asInterface方法

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

查看类名知道public class Binder implements IBinder ,Binder是IBinder的实现类;方法先判断Binder的对象obj是否为null,然后在调用queryLocalInterface()方法,这个方法我们也可以去查看一下源码

    public IInterface queryLocalInterface(String descriptor) {
        if (mDescriptor.equals(descriptor)) {
            return mOwner;
        }
        return null;
    }

如果当前的mDescriptor不等于DESCRIPTOR ,那么直接返回null,相等的话就返回mOwner,想想这个mDescriptor和mOwner分别是什么?就是Binder中的两个变量;我们new Stub的时候是不是为新生成的Stub对象中mOwner和mDescroptor都赋值了;

public class Binder implements IBinder {
    private IInterface mOwner;
    private String mDescriptor;

如果是同一个进程的时候,我们在服务中返回的 new Stub(记住也是Binder),传递到客户端的时候这个Binder对象是不是没有发生任何改变(就是没被序列化,因为没有跨进程),所以我们在客户端调用如下方法的时候,是会返回我们在服务中创建的Binder对象(就是那个Stub)

ICaltotal.Stub.asInterface(iBinder binder)

如果是跨进程的时候,我们在服务中返回的Stub会被序列化之后在传递到服务端,我们在客户端拿到的这个Binder对象,就不是服务端原来的Stub了,到这里我们应该知道客户端有一个Binder,服务端也有一个Binder,跨进程为什么要有两个Binder,想想也是一个进程怎么可能直接操作另一个进程呢,那不成一个进程了?,所以要跨进程,需要Binder这个东东,需要序列化对象,不可能原封不动操作另一个进程的对象,是不是。不扯了,回归正题
那么这个时候在调用ICaltotal.Stub.asInterface(iBinder binder)方法的时候,参数的传递进来的Binder就是另外一个了;被序列化之后的Binder中的成员变量会改变,mOwner变量会null,mDescriptor变量还是”com.csdn.aidl.ICaltotalsum”没有发生改变,因为String类型支持序列化,而IInterface类型没有实现Paracelable,在传递的时候无法被序列化和反序列化.所以最后这个方法会返回null,在回看代码

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

这个就会 返回 new com.csdn.aidl.ICaltotalsum.Stub.Proxy(obj)对象,他就是Proxy代理类的对象;
我们看看它的源码

private static class Proxy implements com.csdn.aidl.ICaltotalsum {
            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 sum(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);
                    mRemote.transact(Stub.TRANSACTION_sum, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

这个类Proxy也实现了ICaltotalsum接口。
看看Proxy的构造方法,它将Binder注入了自身的变量mRemote,在Proxy调用sum方法的时候内部真正发挥作用的是正是Binder对象mRemote;
单单看这个sum方法

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

_data存储客户端的数据,_reply存储服务端返回的数据;
mRemote.transact(Stub.TRANSACTION_sum, _data, _reply, 0);是个跨进程的操作,执行在服务端的Binder线程池里面里面具体细节,我也没研究过,有研究的同学可以让我学习下! 在内部会调用 onTransact()方法,也是运行在Binder的线程池里面;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值