AIDL是Android的IPC方法之一。AIDL使用Binder来实现的。我们新建一个AIDL文件时SDK会根据这个AIDL文件自动帮我们生成一个java文件,实际上AIDL文件只是让我们免去了写一些重复代码的步骤。
AIDL生成的java文件分析
AIDL实现实际上是创建一个Binder对象和一个代理类
这里有一个简单的AIDL文件
interface ISample {
int getNumberOne();
}
自动生成的java文件为这样的
public interface ISample extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.tuivan.test1.ISample {
private static final java.lang.String DESCRIPTOR = "com.example.tuivan.test1.ISample";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.tuivan.test1.ISample interface,
* generating a proxy if needed.
*/
public static com.example.tuivan.test1.ISample asInterface(android.os.IBinder obj) {
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.tuivan.test1.ISample))) {
return ((com.example.tuivan.test1.ISample)iin);
}
return new com.example.tuivan.test1.ISample.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_getNumberOne: {
data.enforceInterface(DESCRIPTOR);
int _result = this.getNumberOne();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.tuivan.test1.ISample {
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 getNumberOne() 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);
mRemote.transact(Stub.TRANSACTION_getNumberOne, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getNumberOne = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public int getNumberOne() throws android.os.RemoteException;
}
可以看到SDK帮我们创建了一个继承自IInterface的ISample接口,这个接口里面含有一个内部抽象类Stub,这个内部类里面又有一个内部类Proxy。
Stub类
由上面可以看到Stub继承自Binder并且要求实现ISample接口,而这个Stub类是用于服务器实现的抽象类,服务器里Service的onBind方法返回的便是这个Stub类。
Stub类中重要方法主要有asInterface和onTransact方法。
先说asInterface方法
public static com.example.tuivan.test1.ISample asInterface(android.os.IBinder obj) {
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.tuivan.test1.ISample))) {
return ((com.example.tuivan.test1.ISample)iin);
}
return new com.example.tuivan.test1.ISample.Stub.Proxy(obj);
}
asInterface方法是由客户端调用的,传入参数是ServiceConnection中返回的IBinder对象,然后这个方法将返回一个我们之前定义的接口对象,这里返回的是ISample对象。
这个方法首先会在当前进程中查找有没有对应的接口对象,如果有则直接返回该接口对象,便不需要用到Proxy类来进行跨进程传递了。如果没有将会新建一个Proxy对象并返回。这个Proxy对象实际上是我们服务器Stub对象的代理,我们通过调用Proxy对象的相应接口方法就可以间接调用Stub对象的相应方法。
Proxy对象间接调用Stub对象方法时需要用到Stub的onTransact方法
@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_getNumberOne: {
data.enforceInterface(DESCRIPTOR);
int _result = this.getNumberOne();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
onTransact方法有4个传入参数,其中code代表需要办理的方法,data和reply分别代表调用该方法时的入参和返回值,因为是跨进程调用,所以用Parcel来存储。
在该方法里面通过switch来根据code的值来执行相应模块,可以看到在TRANSACTION_getNumberOne模块中调用了自身的getNumberOne方法,并且将返回值传入到reply对象中,达到跨进程传输数据的目的。
Proxy类
Proxy类是Stub的代理,在创建Proxy对象时需要传入一个IBinder对象,Proxy类通过调用这个IBinder对象的Transact方法来办理方法的调用。在客户端使用时通过Stub.asInterface静态方法来创建。
这个例子中Proxy类实现了ISample接口,而它的接口实现是这样的
@Override
public int getNumberOne() 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);
mRemote.transact(Stub.TRANSACTION_getNumberOne, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
可以看到它这里调用了创建时的IBinder对象的onTransact方法,并且code为Stub.TRANSACTION_getNumberOne,由上面的分析可以知道这里最终会调用到服务器的ISample实现类的getNumberOne方法,并且会将数据存储在_reply中。
其他
ServiceConnection中返回的IBinder对象实际上并不是服务器中onBind返回的IBinder对象,而是一个BinderProxy对象,由名字可以猜想到该对象是一个代理,也就是说我们通过asInterface得到的ISample接口调用了至少两次onTransact方法才间接调用了服务器的ISapmle对象的相应方法。
通过Binder调用的方法是运行在持有进程的Binder线程池里面的,这里是运行在服务器的Binder线程池中,是需要进行线程同步的,并且客户端调用该方法的线程会挂起,直到服务器处理完毕返回,因此客户端调用不应在UI线程中调用,避免出现ANR。