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的线程池里面;