前面已经写了两篇文章介绍了bound service 的两种方式,现在再来说说第三种方式-aidl。
AIDL
Android 远程接口调用语言和其他远程接口调用语言类似。允许你定义 client-servver 的语言接口来满足两者之间的进程通信(IPC)。在 Android 世界里一个进程通常情况下不能正常的访问另一个进程的内存。如果要访问,你需要将对象分解成最原始的数据以便系统能理解并传递对象穿越进程边界。其中实现的代码是复杂的,所以 Android 使用 AIDL 来为你实现。简单的说,aidl 是为了开发者实现跨进程通信提供的一套规则和工具,自动建 基于 Binder 通信的跨进程交换标准模块
注意:基于 aidl 实现的跨进程通信,再多个客户端同时请求 server 服务时,server 的处理是多线程 并发处理的; 而在前面介绍的 Messenger 实现的 boud service 不是多线程处理的,最终其实走的 handler 的方式。
。
在你开始设计你的AIDL接口之前,你要意识到AIDL接口是直接的方法调用。在哪个线程处理完全取决于调用是来自本地还是远程。具体如下:
*来自本地的调用,执行过程发生在同一个线程里。如果调用发生在UI主线程,则AIDL接口执行也是在主线程。如果调用发生在另一个线程,则AIDL接口的执就在那个线程里。所以如果是本地线程访问service,你完全可以掌控事那个线程在执行(但是这种情况,其实就可以不必使用AIDL了,使用 Binder 获取 service 就行了)。
*调用来自远程进程。如果同时发生多个请求,你无法得知是哪个线程(服务进程中有一个线程池,专门用来处理并发请求的),也就是说AIDL接口必须确保线程安全。
*调用来自远程且实用 oneway关键字,远程调用将不会阻塞,发生transaction data数据之后方法立即返回。如果本地进程调用,oneway关机中无效。
实用AIDL创建bounded service的步骤:
- 创建一饿.aidl文件
在文件中使用java语法定义接口。
- 实现接口。
android SDK 工具会根据.aidl文件生产一个java接口,这个接口包含Stub ,Stub继承自Binder并且实现了这个接口。
- 暴露接口给客户端程序
实现你的 service 并且在你的onBind()方法中返回一个Stub的之类对象。
注意:当你的AIDL接口改变时,确保向前兼容,因为你的AIDL已经向客户端程序发放,确保之前的是可用的。
1 来看看怎么创建.aidl文件
AIDL使用简单的语法使你能够定义接口。参数支持的类型有
*所有java语言的原始数据类型。
*String
*CharSequence
*List,list的元素必须是AIDL支持的数据类型
*HashMap,元素必须是AIDL支持的数据类型
.aidl文件样例:
// IRemoteService.aidl
package com.example.android;
// Declare any non-de;fault types here with import statements
/** Example service interface */
interface IRemoteService {
/** Request the process ID of this service, to do evil things with it. */
int getPid();
/** Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
保存到项目src路径下,将自动生成对应的java文件到gen目录下。自动生成的java文件里面有个Stub类,这个类继承Binder且实现了这个接口,其中有几个重要的实现方法asInterface(),asBinder()等。
2 实现接口
实现AIDl文件里面定义的接口,即实现Stub里面的方法。
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public int getPid(){
return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
// Does nothing
}
};
mBinder 也就是我们的 service 用来 RPC 的 Binder。
注意:
*调用方不保证来自主线程,你要考虑多线程调用的情况,且确保你的service服务方法是线程安全的。
*如果你知道你使用的服务会耗时,你应该避免在主线程调用。
*不要向调用方抛异常。
3 暴露接口给客户端
你需要通过一个service,在 service 的 onBind()方法中将你的 IRemoteService.Stub 实例返回出去,达到将服务接口暴露给客户端的目的。例如:
public class RemoteService extends Service {
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
// Return the interface
return mBinder;
}
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public int getPid(){
return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
// Does nothing
}
};
}
现在,当客户端(一个activity)调用bindService(),客户端的onServiceConnected()回调方法中将受到一个mBinder实例。客户端为了能调用方法,必须保存一份server端发放的.aidl文件在相同目录下,android 工具也会自动生成一份对应的java文件。 通过YourServiceInterface.Stub.asInterface(mBinder),转化成你所要使用的服务接口的实例。例如:
IRemoteService mIRemoteService;
private ServiceConnection mConnection = new ServiceConnection() {
// Called when the connection with the service is established
public void onServiceConnected(ComponentName className, IBinder service) {
// Following the example above for an AIDL interface,
// this gets an instance of the IRemoteInterface, which we can use to call on the service
mIRemoteService = IRemoteService.Stub.asInterface(service);
}
// Called when the connection with the service disconnects unexpectedly
public void onServiceDisconnected(ComponentName className) {
Log.e(TAG, "Service has unexpectedly disconnected");
mIRemoteService = null;
}
};
到此为止,你已经知道怎么使用 aidl 来实现服务接口、客户端怎么获得服务远程代理然后如何调用服务端接口了,后续有时间提供一个 demo。然后再深入研究 Binder 是如何传递数据的、aidl 背后的架构。