AIDL的使用
通过AIDL接口实现下面的功能:
在一个页面中登录,activity只负责接口调用和参数传递,具体实现由service完成,service执行登录后把结果返回给activity。
首先需要新建一个.aidl文件,在里面申明AIDL接口方法:
interface IEcmServiceBinder {
/**
* 登录TF卡
* 初始化加密卡并且获取当前卡的状态
* @param passwd 登录卡的密码
* @return 返回登录结果: xxxxxxxxxx
*/
int[] loginTFCard(String passwd);
}
写好后build一下工程,然后在service端实现这个接口:
public IEcmServiceBinder.Stub mEcmServiceBinder = new IEcmServiceBinder.Stub() {
/**
* 登录TF卡
* 初始化加密卡并且获取当前卡的状态
* @param passwd 登录卡的密码
* @return 返回登录结果: xxxxxxx
*/
@Override
public int[] loginTFCard(String passwd) throws RemoteException {
return xxxxxxxxxxx;
}
}
在activity端,采用bindservice方法启动service,在onServiceConnected中拿到IEcmServiceBinder对象,就可以调用service端的方法了:
private ServiceConnection sc = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IEcmServiceBinder sEcmServiceAIDL = IEcmServiceBinder.Stub.asInterface(service);
sEcmServiceAIDL.loginTFCard("123456");
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
机制分析
先看看编译.aidl文件后生成的IEcmServiceBinder.java,先看个大概,我把里面的具体内容省略了:
还有方法申明和方法对应的常量:
IEcmServiceBinder继承了IInterface,这是一个interface:
IEcmServiceBinder里面有个Stub类,Stub里还有个Proxy类。
我们在service端实现登录方法时是这样写的:
public IEcmServiceBinder.Stub mEcmServiceBinder = new IEcmServiceBinder.Stub() {
............
}
即在service端创建了一个Stub类对象,IEcmServiceBinder.Stub()是调用了Stub的构造函数:
这是Binder中的方法:
Stub本来就继承了Binder,这样是在这个Stub中保存了一个IInterface对象(就是这个Stub),将IInterface与Stub关联起来,之后通过queryLocalInterface()可以取出这个接口(Stub)。
下面看看activity端,onServiceConnected后,通过传来的IBinder拿到AIDL对象:
IEcmServiceBinder sEcmServiceAIDL = IEcmServiceBinder.Stub.asInterface(service);
还是要回到Stub类,它的asInterface()方法:
之前提到,queryLocalInterface()会取出在构造函数中保存的Stub对象,如果这个对象是IEcmServiceBinder的实例,转成IEcmServiceBinder返回;否则new一个Proxy对象返回。
这个地方现在不太明白,网上的说法:
http://blog.youkuaiyun.com/c10wtiybq1ye3/article/details/78362429
怎么区分不同进程的????????
关键在这几行代码:
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.cetcs.encryptcardmanager.EcmService.IEcmServiceBinder))) {
return ((com.cetcs.encryptcardmanager.EcmService.IEcmServiceBinder)iin);
}
return new com.cetcs.encryptcardmanager.EcmService.IEcmServiceBinder.Stub.Proxy(obj);
传了一个字符串常量进去,这个常量是完整类名,假如在其它进程中,可以想象就是其它应用的包名+类名,以此来区分是不是在相同进程。
跟到代码中去,是这样的:
public IInterface queryLocalInterface(String descriptor) {
if (mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
mOwner是这样定义的:
public void attachInterface(IInterface owner, String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
这个方法又是在Stub的构造函数中,把descriptor传进去的:
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
Stub,是在服务端创建的:
这个stub是个静态内部类,直接就构造出来了。在需要判断是不是相同进程时,就通过stub的标签来判断是不是一样的。
那么又要看看Proxy的代码了:
还有一个与AIDL中方法名一样的方法:
先不管Proxy的其它代码,注意图中标红的部分,调用了mRemote的transact方法,transact是Binder的方法:
然后又调用Binder的onTransact:
我们看注释,它需要被override,所以不用关心里面的实现。注意到mRemote是初始化传进来的IBinder对象:
最后一行,那个obj其实是Stub构造函数中存入的Stub本身,这个Stub也override了onTransact方法:
所以通过asInterface返回的这个Proxy,具有和service端一样的方法loginTFCard,通过tracsact调用Stub中的onTransact,下面再看看onTransat:
如果还有其它方法,这里会有很多case。这里传进来了code,data,reply和flags。code就是每个实现方法对应的一个int型常量,data保留了方法调用时传如的参数,在这里是密码“123456”,reply中保留了方法调用的结果。这里调用到了this.loginTFCard(_arg0),this就是Stub,是一开始在service端创建的:
public IEcmServiceBinder.Stub mEcmServiceBinder = new IEcmServiceBinder.Stub() {
xxxxxxxxxxxxxxxxxxxxxxxxx
}
这样回到了service端,可以调用里面的方法了。
所以,我们通过IEcmServiceBinder.Stub.asInterface(service),拿到AIDL的代理类Proxy,Proxy通过transact,调用Stub的onTransact,间接调用到service端的代码了。