在上篇文章Android 多进程通信(2) --- Service:Binder、Messenger中介绍了Service通信的几种常用方式,包括:
- 扩展Binder类:只在本App中使用,且与客户端运行在相同进程下;
- 使用Messenger:进程间通信,但不需要处理多线程;
- 使用AIDL:进程间通信,需要处理多线程操作;
前两种通信方式在上篇文章中有详细的介绍,且附有简单易懂的例子,本文主要对AIDL通信进行分析介绍。
IDL
IDL:Interface Description Language (接口定义语言),是一种描述软件组件接口的语言规范。用一种中立的方式来描述接口,使得在不同平台上运行的对象和用不同语言编写的程序可以相互通信交流。
AIDL
AIDL:Interface Description Language(Android接口定义语言),与其他IDL类似,但是用来定义客户端与服务端进行进程间通信(IPC)时的接口。在Android系统中,想要进行IPC通信,即一个进程与另一个进程通信,是一项复杂的操作,Android会使用AIDL来处理。(具体来说,Android中一个进程无法访问另一个进程内存,要进行IPC,进程需要将其对象分解成操作系统能够识别的原语,并将对象编组成跨越边界的对象)
AIDL 语法
与Java语法基本一样,只有一些区别:
- 文件类型:AIDL文件的后缀是 .aidl
- 数据类型:除了AIDL默认支持的数据类型,其他所有类型的使用都需要导包,不论其位置在哪里。默认支持的数据类型:
(1)Java的8种基本数据类型:byte, short, int, long, float, double, boolean, char
(2)String和CharSequence类型
(3)List和Map类型:集合中项的允许数据类型包括Java原始类型、String、CharSequence或是android.os.Parcelable。无需为List和Map提供import语句,但需要为Parcelable提供import语句
(4)其他AIDL接口:引用的每个AIDL接口都需要一个import语句,即使位于同一个包中
(5)实现了android.os.Parcelable接口的复杂类型 - 定向Tag:非原始类型中,除了String和CharSequence以外,其余均需要一个方向指示符。方向指示符包括in、out、和inout。in表示由客户端设置,out表示由服务端设置,inout表示客户端和服务端都设置了该值。默认为 in,不能是其他方向。
- 两种AIDL文件:
(1)定义Parcelable对象:供其他AIDL文件使用AIDL中非默认支持的数据类型的
(2)定义方法接口:供系统使用来完成跨进程通信
使用AIDL创建绑定服务
- 创建 .aidl 文件
右击项目new aidl,创建后缀为aidl的文件,并在该文件中定义带有方法签名的编程接口;
interface IRemoteService {
String getAnswer();
String getQuestion();
}
- 实现接口
编译项目,Android SDK会根据aidl文件自动生成一个IBinder接口,且该接口包含一个Stub内部抽象类,该类是父接口(YourInterface.Stub)的抽象实现,可以用来扩展Binder类并实现aidl接口中的方法;
Stub
还定义了几个帮助程序方法,其中最引人关注的是asInterface()
,该方法带IBinder
(通常便是传递给客户端onServiceConnected()
回调方法的参数)并返回存根接口实例。
定义aidl接口后,需要实现该接口才能使用,通过扩展生成的 Binder 接口(例如 YourInterface.Stub)并实现从 aidl 文件中继承的方法。
private final IRemoteService.Stub stub = new IRemoteService.Stub() {
@Override
public String getAnswer() throws RemoteException {
return answerName();
}
@Override
public String getQuestion() throws RemoteException {
return question();
}
};
注:由于不能保证在主线程上执行传入调用,因此这里需要注意并将服务设计为线程安全的
- 客户端调用该接口
实现Service并重写onBind(),返回Stub类的实现
@Nullable
@Override
public IBinder onBind(Intent intent) {
return stub;
}
此时,当客户端调用Service的bindService()
时,onServiceConnected()
回调会接收Service的onBind()
方法返回的IBinder对象。当客户端在 onServiceConnected()
回调中收到 IBinder
时,通过调用YourServiceInterface.Stub.asInterface(service)
以将返回的参数转换成 YourServiceInterface
类型。
IRemoteService iRemoteService;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder iBinder) {
iRemoteService = IRemoteService.Stub.asInterface(iBinder);
}
@Override
public void onServiceDisconnected(ComponentName name) {
iRemoteService = null;
}
};
注:客户端还需要有对 interface 类的访问权限,因此如果客户端和服务在不同的应用内,则客户端的目录内必须包含 .aidl 文件的副本。
小结
关于Service的多进程通信到此就基本介绍完毕了,关于以上完整代码见github代码,且该代码需要切换分支查看,分支名为service-aidl