面试官开口说:“听你刚才所说,在Android系统中,都是利用Binder来进行进程间通信的,那我怎么听说,还有利用AIDL来实现进程间通信的呢?”。
其实,AIDL只是一种描述性语言,其全称是Android Interface Definition Language,即接口定义语言,利用ADT,我们可将自定义的AIDL文件转化成Java代码,而这些代码就能够来进行进程间通信(IPC)。为什么这些代码就能够进行IPC呢?那是因为,这些代码就是定义了Binder机制中作为服务端的Binder对象和客户端中用的Proxy对象。
在前面的文章中,我们讲过,作为服务端的服务,会在本线程中创建一个Binder对象,并将其引用传递给Binder驱动,而客户端通过Binder驱动获得对应的Binder对象的引用时,其获得的其实是一个Proxy对象,然后其通过这个Proxy对象去跟驱动,再由驱动去跟服务端的Binder对象进行通信。
而通过AIDL生成的代码,我们就可以从代码的层面来帮助我们更好地理解关于Binder机制的作用了。
看来,面试变成了上机操作。。。
我们还是先写一份AIDL文件,定义一个接口和对应的方法。
首先,我为什么是定义接口呢?仔细想想,其实在不同的进程中进行通信,无非就是想使用彼此的服务,而服务的一个非常好的表现形式就是对接口编程,也就是说,我们只需要知道服务提供的名称是什么,需要什么参数,而具体服务端怎么实现,我们并不关心。当服务端改变了实现,它也只需要保持接口的一致性,就不会影响客户端的使用。
package com.lms.aidl;
import java.util.List;
import com.lms.aidl.Bean;
interface ITestService {
List getBean();
void addBean(in Bean bean);
}
利用ADT插件,在Eclipse中会生成如下的java代码
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: ...\\AidlServer\\src\\com\\lms\\aidl\\ITestService.aidl
*/
package com.lms.aidl;
public interface ITestService extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements
com.lms.aidl.ITestService {
private static final java.lang.String DESCRIPTOR = "com.lms.aidl.ITestService";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.lms.aidl.ITestService interface,
* generating a proxy if needed.
*/
public static com.lms.aidl.ITestService asInterface(
android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.lms.aidl.ITestService))) {
return ((com.lms.aidl.ITestService) iin);
}
return new com.lms.aidl.ITestService.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_getBean: {
data.enforceInterface(DESCRIPTOR);
java.util.List _result = this.getBean();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBean: {
data.enforceInterface(DESCRIPTOR);
com.lms.aidl.Bean _arg0;
if ((0 != data.readInt())) {
_arg0 = com.lms.aidl.Bean.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBean(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.lms.aidl.ITestService {
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 java.util.List getBean()
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBean, _data, _reply, 0);
_reply.readException();
_result = _reply
.createTypedArrayList(com.lms.aidl.Bean.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addBean(com.lms.aidl.Bean bean)
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((bean != null)) {
_data.writeInt(1);
bean.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBean, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getBean = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBean = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.util.List getBean()
throws android.os.RemoteException;
public void addBean(com.lms.aidl.Bean bean)
throws android.os.RemoteException;
}
1)接口ITestService和其对应的方法
2)Stub类
3)Proxy类
其中,Stub类是一个抽象类,继承了Binder类,并且实现了ITestService。其实这就是在我们定义服务端服务时需要去实现的Binder类,也就是说,当我们创建一个服务,并且希望这个服务能够被跨进程使用的话,我们就可以在我们的服务中去实现这样一个Stub类,在其定义的方法中去实现对应的逻辑,这个我在下一篇文章中会给出对应的例子。
而在Stub类中呢,又定义了一个Proxy类,同样也实现了ITestService接口,所以对于客户端进程来说,其与Proxy通信,感觉就会像跟服务器端的Binder类通信一样,因为两边暴露出来的方法都是一样的,这也是设计模式中代理模式的一个非常典型的应用。
而实际上Proxy类则是通过一个IBinder类型的mRemote对象来跟驱动进行交互,并将对应的数据信息通过驱动与服务端的进程进行交互,而Android的Binder类,其实也是实现了IBinder接口,如下:
public class Binder implements IBinder {
/*
* Set this flag to true to detect anonymous, local or member classes
* that extend this Binder class and that are not static. These kind
* of classes can potentially create leaks.
*/
private static final boolean FIND_POTENTIAL_LEAKS = false;
private static final String TAG = "Binder";
private int mObject;
说了这么多次服务端的是用Stub类,也即Binder对象,客户端的是使用Proxy类,虽然Proxy类中也是利用mRemote这个Binder接口,那么,在具体的进程中,比如就是A进程,它是怎么去判断拿哪个对象呢?到底是拿Stub类呢,还是拿Proxy类呢?
这一点,我们也可以从上面这份代码中看出来哦!面试官好像感觉有点意思的样子呢!!!!
我们在Stub类中可以看到如下方法:
/**
* Cast an IBinder object into an com.lms.aidl.ITestService interface,
* generating a proxy if needed.
*/
public static com.lms.aidl.ITestService asInterface(
android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.lms.aidl.ITestService))) {
return ((com.lms.aidl.ITestService) iin);
}
return new com.lms.aidl.ITestService.Stub.Proxy(obj);
}
或者,我们再看进去Binder类中的queryLocalInterface方法,
public IInterface queryLocalInterface(String descriptor) {
if (mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
public void attachInterface(IInterface owner, String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
而我们从Stub的构造函数中可以看到,owner就是我们实现的某个Stub对象,对吧。
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
static final int TRANSACTION_getBean = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBean = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
啊!差点忘了,onTransact方法是干什么的?
其实呀,从上面AIDL生成的这份代码中,我们就可以看到,onTransact方法其实主要就是在用户空间和内核空间中进行数据的交换,也就是实现进程间数据的交互,从而来通知彼此应该要干什么事。
传递的数据都在Parcel参数data和reply中,关于这些,我觉得无非就是数据交互所定的协议和规范,读取顺序之类的东西,好像真要讲,我得去多多补充知识才能讲得清楚。
太晚了,面试官都睡着了,这打呼的声音。。。。
稍微总结一下,AIDL文件,其实是一份辅助的文件,因为有了ADT的存在,其才能发挥作用,因为ADT能够根据我们定义的AIDL文件,生成对应的Stub类和Proxy类等Binder机制相关的代码。
所以,对于一些大牛来说,完全可以不需要AIDL文件,直接就可以写出ADT生成的这些代码,并实现进程间的通信,所以AIDL,只是简化IPC开发的一个小工具而已,其实跟IPC本身并没有什么关系。
当然也就不能说利用AIDL来实现IPC,最多只能说,利用AIDL和ADT来实现Binder机制,从而实现IPC。