本文翻译自android官方文档,结合自己测试,整理如下。
Android Interface Definition Language(AIDL)能够让我们定义自己的编程接口,该接口可以使得客户端和service之间进行跨进程通信(interprocess communication,IPC)。通常,在android中无法直接跨进程通信。因此,需要把传递的对象分解成系统可以识别的原始状态(数据),并将它们跨进程序列化marshalling。由于marshalling过程繁琐,因此android通过AIDL处理。
注意:只有当我们允许不同应用程序的客户端获取service来进行IPC,并且在service中需要处理多线程时,AIDL才是必须的。绝大多数应用程序都不应该用AIDL来创建Bound Service,因为这可能需要多线程处理能力并且会让代码变得更为复杂。因此,AIDL对绝大多数应用程序都不适用。如果只是在应用程序内部使用,并且不需要跨进程,我们可以通过继承Binder类直接进行交互,这种是最常见的方式。若跨进程IPC且不需要处理多线程问题可以通过使用Messenger方法,因为Messenger把所有请求都放在一个线程中,因此不必担心线程安全问题。
(下面的理解上还有些问题)在开始设计AIDL接口之前,需要注意的是调用AIDL接口是直接的调用方法,我们不应该假设调用方法发生在子线程。从本地进程和远程进程中的线程调用是不同的:
上述内容理解有的问题,须再查阅资料并验证,同时请各位不吝赐教。
AIDL接口必须定义在.aidl
文件中(命名满足java语法),并同时保存在service所在的应用程序和其它绑定该service的应用程序(需要通过AIDL进行IPC的service)中,保存位置为源代码中src/
目录下。
当我们新建一个.aidl
文件时,android SDK工具就会根据该文件自动生成一个IBinder接口,并且保存在gen/
目录下。service必须实现IBinder接口,客户端才能绑定service并调用方法获得该对象进行IPC。
上面的目录是在eclipse中的,在android studio中则在:
为了能够创建使用AIDL的service,必须要实现以下步骤:
.aidl
文件 .aidl
文件。这个接口中有一个内部抽象类Stub,该类继承了Binder类,并且实现了AIDL接口中的方法,我们必须继承Stub类和实现其方法。onBind()
方法,并且返回Stub的实现。注意:在我们第一次发布之后的改变AIDL接口都要保证对原来的版本的兼容性,避免其它应用无法访问我们的service(其它客户端可能拷贝的还是原来的接口版本)。
下面详细介绍以上几步:
.aidl
文件AIDL需要满足一些简单的语法:能够使我们声明一个接口,该接口可以包含一个或多个方法,且能够带有参数和返回值。参数和返回值可以是任何类型的甚至是其它AIDL生成的接口。
每个.aidl
文件必须定义一个接口,并且只需要接口声明和方法声明。
默认情况下,AIDL支持以下数据类型:
若是使用上述以外的类型(例如自定义类)必须导入相关声明,即使在同一个包中定义的。
在定义AIDL接口时,需要注意以下几点:
下面有一个例子:
package com.sywyg.servicetest;
import com.sywyg.servicetest.Man;
// Declare any non-default types here with import statements
// 需要导入自定义类型的位置
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);
// Man getMan();
}
然后系统就会自动生成一个IRemoteService.java(对应IRemoteService.aidl)文件。
有的编译器是立刻生成,有的则在编译应用程序时生成,这点注意。
自动生成文件包含一个内部抽象类Stub,继承了Binder并是父类接口的一个抽象实现,实现了.aidl
文件中的所有方法。Stub同时定义了一些其他有用的方法,尤其是asInterface()
方法,该方法接收一个IBinder对象,返回Stub接口的实现。Stub英文表示存根的意思,该类在服务端进程,我们必须继承该类并实现aidl接口中的方法。
下面用一个匿名类实现简单的接口实例:
/**
* 定义一个匿名内部类实现aidl接口,需要继承IRemoteService.Stub类
* 在.aidl文件中声明的方法需要在这里实现具体功能
*/
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public int getPid(){
Log.d(TAG,"getPid is executed ...");
return android.os.Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
Log.d(TAG,"basicTypes is executed ...");
}
};
Stub实现了Binder类(定义了远程过程调用协议Remote Procedure Call Protocol RPC),因此mBinder可以传输给客户端。
在实现AIDL时需要注意一下几点:
为service实现了AIDL接口,我们应该把接口暴露给客户端,使得他们能够绑定它。下面给出完整的代码,说明如何实现:
/**
* 通过AIDL实现IPC
* @author sywyg
* @since 2015.7.16
*/
public class AIDLService extends Service {
private final String TAG = "result";
public AIDLService() {
}
/**
* 定义一个匿名内部类实现aidl接口,需要继承IRemoteService.Stub类
* 在.aidl文件中声明的方法需要在这里实现具体功能
*/
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public int getPid(){
Log.d(TAG,"getPid is executed ...");
return android.os.Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
Log.d(TAG,"basicTypes is executed ...");
}
};
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
那么,当客户端调用bindService()
连接service时,客户端回调onServiceConnected()
方法接收mBinder实例(service的onBinder()
方法返回的)。
客户端必须也能够获得该接口类,因此当客户端和service在不同的应用程序时,客户端应用程序必须复制一份.aidl
文件,这样才能获得AIDL中的方法。
当客户在onServiceConnected()
方法中接收IBinder对象时,必须通过调用YourServiceInterface.Stub.asInterface(service)
转换为YourServiceInterface类型。如下:
IRemoteService mIRemoteService;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG,"绑定成功");
// Following the example above for an AIDL interface,
// this gets an instance of the IRemoteService, which we can use to call on the service
// 还是接着上面的例子,通过这种方式获得IRemoteService的一个实例,
// 这样,我们可以在客户端进行处理了。
mIRemoteService = IRemoteService.Stub.asInterface(service);
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
mBound = false;
}
};
我们可以实现通过IPC把对象从一个进程传递到另一个进程中。但是,我们必须要确保在另一个进程中可以获得该对象(即需要该类的代码),并且该类需要支持Parcelable接口。必须要支持Parcelable,这样系统才能将对象分解为基本数据类型(能够跨进程marshalled)。
注意:Parcelable是一个接口,实现该接口的类实例能够保存在Parcel中并从中恢复。该类中必须有一个名叫CREATOR的静态成员变量,该成员是Parcelable.Creator的一个实现实例。
为了创建支持Parcelable协议的类,必须完成以下几点:
writeToParcel()和
方法,记录当前对象的状态(成员变量等),并用Parcel保存。还要实现describeContents()
,一般返回0;.aidl
文件声明该parcelable类(例如下面的Rect.aidl文件)。若你正在进行自定义生成过程,不要添加.aidl
文件,这是因为,它类似C语言的头文件,不会进行编译的。???茫然AIDL通过上述办法产生marshall和unmarshall对象。
下面是一个实现Parcelable接口的类Rect,首先要有Rect.aidl文件:
package android.graphics;
// Declare Rect so AIDL can find it and knows that it implements
// the parcelable protocol.
// 声明Rect,AIDL好找到并确认它实现了parcelable协议
parcelable Rect;
下面是Rect类:
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();
}
}
Parcel同样可以写其它类型的数据。
警告:不要忘记从另一个进程中获取数据时的安全问题。上面的例子,Rect获取四个数,但是这取决于你获得多少数据(读写顺序要一致)。
客户端必须完成以下步骤才能实现调用远程接口:
.aidl
文件。Context.bindService()
,并传递ServiceConnection的实现。onServiceConnected()
实现中,我们可以接受IBinder的一个实例(名为service)。调用asInterface()
转换成接口实例。Context.unBindService()
解除连接。调用IPC service注意事项:
原文地址:http://blog.csdn.net/wangyongge85/article/details/46907599