AIDL使用介绍
AIDL的全称是Android Interface definition language,一看就明白,它是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口,用处当然就是用来进程间的通信和方法调用了。其实AIDL是Binder的一个上层实现,它简化了Binder的使用,在编译时,由编译器帮助我们完成了大量工作(例如,生成AIDL文件所对应的.java类)。
先介绍一下AIDL进程间通信的流程。
AIDL通信流程简介
1. AIDL接口的创建
AIDL文件中,并不是所有的数据类型都是可以使用的,它支持的数据类型有:
- 基本数据类型(int,long,char,boolean,double等)
- String和CharSequence
- List:只支持ArrayList,而且list中的元素也必须是 AIDL 支持的类型
- Map:只支持HashMap,里面的key和value也必须是AIDL支持的类型
- Parceable:所有实现了 Parceable 接口的对象
- AIDL:所有的 AIDL 接口本身也可以在 AIDL 文件中使用,所以 IBinder 类型也是支持的
2. 服务端
服务端首先要创建一个 Service 用来监听客户端的请求,然后将在对应AIDL文件中声明的接口实现,并且通过onbind函数返回相应 IBinder 对象即可。
3. 客户端
客户端所要做的事情就稍微简单一些,首先需要绑定服务端的Service,绑定成功后,将服务端返回的 IBinder 对象转成AIDL接口所属的类型,接着就可以调用AIDL中的方法了。
AIDL文件及参数说明
自定义类型
通常,AIDL默认支持基础类型参数及返回值的数据传递。如果AIDL文件中用到了自定义的Parcelable对象,那么必须新建一个和它同名的AIDL文件,并在其中声明它为Parcelable类型。
例如,我们定义了一个名为RequestData的数据类型,要想在AIDL中使用它,除了它自己的RequestData.java文件之外,我们还必须定义一个名为RequestData.aidl的文件(包名必须和RequestData.java中的相同),内容如下:
package com.testaidl;
parcelable RequestData;
参数标志
AIDL中除了基本数据类型,其他类型的参数必须标上方向:in,out或者inout, in表示输入型参数,out表示输出型参数,inout表示输入输出型参数。
我们要根据实际需要去指定参数类型,不能一概使用out或者inout,因为这在底层实现是有开销的。
接口及同步
AIDL接口中只支持方法,不支持声明静态常量,这一点区别于传统的接口。
客户端调用服务端方法,被调用的方法运行在服务端的Binder线程池中,同时客户端线程会被挂起,这个时候如果服务端方法执行比较耗时,就会导致客户端线程长时间阻塞,而如果这个客户端线程是UI线程的话,就会导致客户端ANR,所以如果知道服务端的一个方法是耗时的,就要避免在客户端的UI线程中去调用该远程方法。
但是有一种方法可以发起非阻塞式的远程调用:就是在声明AIDL时,加上oneway关键字。
相比平常自定义的 aidl,多了 oneway 的关键字,声明和不声明 oneway 关键字的在于生成 Java 类中一个参数:
不声明 oneway 时,mRemote.transact 传入的最后一个参数是 0;声明 oneway 时,mRemote.transact 传入的最后一个参数是 android.os.IBinder.FLAG_ONEWAY 。
FLAG_ONEWAY 的作用就是让客户端能够非阻塞的调用远程方法。
系统Service的调用示例
我们以系统服务的调用来分析AIDL的使用过程。
系统服务的调用过程
首先通过 ServiceManager.getService()方法获取一个IBinder对象,但是这个IBinder对象不能直接调用,必须要通过asInterface方法转成对应的IInterface对象才可以使用,如果在同一个进程中(当然,这是跨进程通信,这种情况很少),就会直接返回通过attachInterface方法设置的IInterface对象(上面代码所述),但是如果不是在同一个进程中,就会先通过IBinder对象创建一个Proxy对象(服务的本地代理对象),然后在Proxy对象中通过调用IBinder对象的transact方法调用到服务进程的Service中,实现了跨进程的通信。
以AMS为例,我们来看具体实现。
AMS的服务调用关键实现
AIDL 文件生成的类中会自动生成两个类,Proxy 类和 Stub 类,对应的是 ActivityManagerProxy 和 ActivityManagerService 类。
我们使用ActivityManager实现AMS通信时,在其内部都是调用ActivityManagerNative来实现的。我们来看相关类的继承关系:
我们在应用层调用,都会调用到ActivityManagerNative.getDefault()方法,我们来看源码:
static public IActivityManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IActivityManager in =
(IActivityManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
return new ActivityManagerProxy(obj);
}
static public IActivityManager getDefault() {
return gDefault.get();
}
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};
逻辑解析:
- getDefault返回一个Singleton对象。
- ServiceManager.getService(“activity”)返回AMS的IBinder对象,该对象需要转换后使用。
- asInterface()方法把AMS的IBinder对象转换为可供使用的对象。
asInterface()方法调用结果会有2种情况,第一种是,如果调用者和被调用服务在同一个进程,则进行本地通信,直接返回当前对象即可;第二种是,如果调用者和被调用服务是在不同进程,则会通过服务的BinderProxy实现跨进程调用。我们分别来分析。
同一个进程
queryLocalInterface 函数如果不为null,则在同一进程中通信。
queryLocalInterface 函数:
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;
}
public ActivityManagerNative() {
attachInterface(this, descriptor);
}
mOwner是在ActivityManagerNative创建的时候赋值的,就是一个ActivityManagerNative对象。AMS的初始化是在SystemServer进程中,所以ActivityManagerNative构造函数是在SystemServer进程中调用的。
不同进程
如果queryLocalInterface 方法返回的是 null,就会创建一个ActivityManagerProxy对象(参数是IBinder对象),该对象是服务端AMS的代理对象,可跨进程通信。
return new ActivityManagerProxy(obj);
我们来看ActivityManagerProxy的部分实现:
class ActivityManagerProxy implements IActivityManager
{
public ActivityManagerProxy(IBinder remote)
{
mRemote = remote;
}
public IBinder asBinder()
{
return mRemote;
}
public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
...
mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
reply.readException();
...
}
}
可以看到,ActivityManagerProxy 的作用只是作为代理而已,通过它,最终会调远程(Service端)mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0)来实现具体的功能。
到了这里,我们明白,如果是远程调用,即Client和Server不在同一个进程中,queryLocalInterface必然返回null,这样就会new一个ActivityManagerProxy对象来进行远程调用。