使用AIDL设计远程接口(Designinga Remote Interface Using AIDL)
由于每个应用程序都运行在自己的进程空间,并且可以从应用程序UI运行另一个服务进程,而且经常会在不同的进程间传递对象。在Android平台,一个进程通常不能访问另一个进程的内存空间,所以要想对话,需要将对象分解成操作系统可以理解的基本单元,并且有序的通过进程边界。
通过代码来实现这个数据传输过程是冗长乏味的,Android提供了AIDL工具来处理这项工作。
AIDL (Android Interface Definition Language)是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行进程间通信(IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。
AIDL IPC机制是面向接口的,像COM或Corba一样,但是更加轻量级。它是使用代理类在客户端和实现端传递数据。
Note: Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL(这个去bound Service中看demo)
Before you begin designing your AIDL interface, be aware that calls to an AIDL interface are direct function calls. You should not make assumptions about the thread in which the call occurs. What happens is different depending on whether the call is from a thread in the local process or a remote process. Specifically:
- Calls made from the local process are executed in the same thread that is making the call. If this is your main UI thread, that thread continues to execute in the AIDL interface. If it is another thread, that is the one that executes your code in the service. Thus, if only local threads are accessing the service, you can completely control which threads are executing in it (but if that is the case, then you shouldn't be using AIDL at all, but should instead create the interface by implementing a Binder).
- Calls from a remote process are dispatched from a thread pool the platform maintains inside of your own process. You must be prepared for incoming calls from unknown threads, with multiple calls happening at the same time. In other words, an implementation of an AIDL interface must be completely thread-safe.
- The
oneway
keyword modifies the behavior of remote calls. When used, a remote call does not block; it simply sends the transaction data and immediately returns. The implementation of the interface eventually receives this as a regular call from theBinder
thread pool as a normal remote call. Ifoneway
is used with a local call, there is no impact and the call is still synchronous.
定义一个AIDL接口
使用AIDL实现IPC服务的步骤是:
1. 创建.aidl文件-该文件(YourInterface.aidl)定义了客户端可用的方法和数据的接口。
2. 在makefile文件中加入.aidl文件-(Eclipse中的ADT插件提供管理功能)Android包括名为AIDL的编译器,位于tools/文件夹。
3. 实现接口-AIDL编译器从AIDL接口文件中利用Java语言创建接口,该接口有一个继承的命名为Stub的内部抽象类(并且实现了一些IPC调用的附加方法),要做的就是创建一个继承于YourInterface.Stub的类并且实现在.aidl文件中声明的方法。
4. 向客户端公开接口-如果是编写服务,应该继承Service并且重载Service.onBind(Intent)以返回实现了接口的对象实例
第一步中要注意的参数只能是基本类型和实现Parcel接口的,在后面详细说。
第二步实现接口中,AIDL生成了与.aidl文件同名的接口,如果使用Eclipse插件,AIDL会做为编译过程的一部分自动运行(不需要先运行AIDL再编译项目),如果没有插件,就要先运行AIDL(命令行, adil path\SomeService.adil,没成功,继续查找中)
生成的接口包含一个名为Stub的抽象的内部类,该类声明了所有.aidl中描述的方法,Stub还定义了少量的辅助方法,尤其是asInterface(),通过它或以获得IBinder(当applicationContext.bindService()成功调用时传递到客户端的onServiceConnected())并且返回用于调用IPC方法的接口实例,更多细节参见Callingan IPC Method。
要实现自己的接口,就从YourInterface.Stub类继承,然后实现相关的方法(可以创建.aidl文件然后实现stub方法而不用在中间编译,Android编译过程会在.java文件之前处理.aidl文件)。
一个简单的小例子,
private final IRemoteService.Stub mBinder = enw IRemoteService.Stub(){
public int getPid(){
return Process.myPid();
}
}
最后一步向客户端公开接口,也就是发布服务,实现的方法是继承 Service,然后实现以Service.onBind(Intent)返回一个实现了接口的类对象。下面的代码片断表示了暴露IRemoteService接口给客户端的方式。
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 } }; }
同时客户端在绑定service会有个onServiceConnected()的回调,在拿到其中的IBinder,可以通过 YourServiceInterface.Stub.asInterface(ibinder)来获取 YourServiceInterface接口,随后可以对其中的方法进行调用,原来Stub类就是继承于Binder类
这里是实现接口的几条说明:
l 不会有返回给调用方的异常
l 默认IPC调用是同步的。如果已知IPC服务端会花费很多毫秒才能完成,那就不要在Activity或View线程中调用,否则会引起应用程序挂起(Android可能会显示“应用程序未响应”对话框),可以试着在独立的线程中调用。
AIDL接口中只支持方法,不能声明静态成员。调用IPC方法(Callingan IPC Method)
这里给出了调用远端接口的步骤:
1. 声明.aidl文件中定义的接口类型的变量。
2. 实现ServiceConnection
3. 调用Context.bindService(),传递ServiceConnection的实现
4. 在ServiceConnection.onServiceConnected()方法中会接收到IBinder对象,调用YourInterfaceName.Stub.asInterface((IBinder)service)将返回值转换为YourInterface类型
5. 调用接口中定义的方法。应该总是捕获连接被打断时抛出的DeadObjectException异常,这是远端方法唯一的异常。
6. 调用Context.unbindService()断开连接
这里是几个调用IPC服务的提示:
l 对象是在进程间进行引用计数
l 可以发送匿名对象作为方法参数
这里的demo就是不写了,可以参照binder中的apidemo代码
最后,说说Parcel,这个也是个轻量级的序列化的类型。说是Android提供的一个比Serializable效率更高的序列化类(未验证),两者都可以在Bundle中使用。
Parcelable需要实现三个函数
1) void writeToParcel(Parcel dest, int flags) 将需要序列化存储的数据写入外部提供的Parcel对象dest。读取Parcel数据的次序要和这里的write次序一致,否则可能会读错数据。
2) describeContents() 没搞懂有什么用,反正直接返回0也可以
3) static final Parcelable.Creator对象CREATOR 这个CREATOR命名是固定的,而它对应的接口有两个方法:
createFromParcel(Parcel source) 实现从source创建出JavaBean实例的功能
newArray(int size) 创建一个类型为T,长度为size的数组,仅一句话(return new T[size])即可。估计本方法是供外部类反序列化本类数组使用。
import android.os.Parcel; import android.os.Parcelable; public final class Rect implements Parcelable { public int left; public int top; public int right; public int bottom; public static final Parcelable.Creator<Rect> CREATOR = new Parcelable.Creator<Rect>() { public Rect createFromParcel(Parcel in) { return new Rect(in); } public Rect[] newArray(int size) { return new Rect[size]; } }; public Rect() { } private Rect(Parcel in) { readFromParcel(in); } public void writeToParcel(Parcel out) { out.writeInt(left); out.writeInt(top); out.writeInt(right); out.writeInt(bottom); } public void readFromParcel(Parcel in) { left = in.readInt(); top = in.readInt(); right = in.readInt(); bottom = in.readInt(); } }
package android.graphics; // Declare Rect so AIDL can find it and knows that it implements // the parcelable protocol. parcelable Rect;
http://developer.android.com/guide/developing/tools/aidl.html#calling