[文章内容来自Developers]javascript
AIDL(Android 接口定义语言)与您可能使用过的其余 IDL 相似。 您能够利用它定义客户端与服务使用进程间通讯 (IPC) 进行相互通讯时都承认的编程接口。 在 Android 上,一个进程一般没法访问另外一个进程的内存。 尽管如此,进程须要将其对象分解成操做系统可以识别的原语,并将对象编组成跨越边界的对象。 编写执行这一编组操做的代码是一项繁琐的工做,所以 Android 会使用 AIDL 来处理。html
注:只有容许不一样应用的客户端用 IPC 方式访问服务,而且想要在服务中处理多线程时,才有必要使用 AIDL。 若是您不须要执行跨越不一样应用的并发 IPC,就应该经过实现一个 Binder建立接口;或者,若是您想执行 IPC,但根本不须要处理多线程,则使用 Messenger 类来实现接口。不管如何,在实现 AIDL 以前,请您务必理解绑定服务。java
在您开始设计 AIDL 接口以前,要注意 AIDL 接口的调用是直接函数调用。 您不该该假设发生调用的线程。 视调用来自本地进程仍是远程进程中的线程,实际状况会有所差别。 具体而言:android
您必须使用 Java 编程语言语法在 .aidl文件中定义 AIDL 接口,而后将它保存在托管服务的应用以及任何其余绑定到服务的应用的源代码(src/目录)内。
您开发每一个包含 .aidl文件的应用时,Android SDK 工具都会生成一个基于该 .aidl文件的 IBinder接口,并将其保存在项目的 gen/目录中。服务必须视状况实现 IBinder接口。而后客户端应用即可绑定到该服务,并调用 IBinder中的方法来执行 IPC。
如需使用 AIDL 建立绑定服务,请执行如下步骤:编程
注意:在 AIDL 接口首次发布后对其进行的任何更改都必须保持向后兼容性,以免中断其余应用对您的服务的使用。 也就是说,由于必须将您的 .aidl文件复制到其余应用,才能让这些应用访问您的服务的接口,所以您必须保留对原始接口的支持。api
1. 建立 .aidl 文件
AIDL 使用简单语法,使您能经过可带参数和返回值的一个或多个方法来声明接口。 参数和返回值能够是任意类型,甚至能够是其余 AIDL 生成的接口。
您必须使用 Java 编程语言构建 .aidl文件。每一个 .aidl文件都必须定义单个接口,而且只需包含接口声明和方法签名。
默认状况下,AIDL 支持下列数据类型:安全
您必须为以上未列出的每一个附加类型加入一个 import语句,即便这些类型是在与您的接口相同的软件包中定义。
定义服务接口时,请注意:多线程
注意:您应该将方向限定为真正须要的方向,由于编组参数的开销极大。并发
如下是一个 .aidl
文件示例:app
// IRemoteService.aidlpackage com.example.android;
// Declare any non-default 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);}复制代码
只需将您的 .aidl文件保存在项目的 src/目录内,当您开发应用时,SDK 工具会在项目的 gen/目录中生成 IBinder接口文件。生成的文件名与 .aidl文件名一致,只是使用了 .java扩展名(例如,IRemoteService.aidl生成的文件名是 IRemoteService.java)。
若是您使用 Android Studio,增量编译几乎会当即生成 Binder 类。 若是您不使用 Android Studio,则 Gradle 工具会在您下一次开发应用时生成 Binder 类 — 您应该在编写完 .aidl文件后当即用 gradle assembleDebug(或 gradle assembleRelease)编译项目,以便您的代码可以连接到生成的类。
2.实现接口
当您开发应用时,Android SDK 工具会生成一个以 .aidl文件命名的 .java接口文件。生成的接口包括一个名为 Stub的子类,这个子类是其父接口(例如,YourInterface.Stub)的抽象实现,用于声明 .aidl文件中的全部方法。
注:Stub还定义了几个帮助程序方法,其中最引人关注的是 asInterface(),该方法带 IBinder(一般即是传递给客户端 onServiceConnected()回调方法的参数)并返回存根接口实例。 如需了解如何进行这种转换的更多详细信息,请参见调用 IPC 方法。
如需实现 .aidl生成的接口,请扩展生成的 Binder接口(例如,YourInterface.Stub)并实现从 .aidl文件继承的方法。
如下是一个使用匿名实例实现名为 IRemoteService的接口(由以上 IRemoteService.aidl示例定义)的示例:
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是 Stub类的一个实例(一个 Binder),用于定义服务的 RPC 接口。 在下一步中,将向客户端公开该实例,以便客户端能与服务进行交互。
在实现 AIDL 接口时应注意遵照如下这几个规则:
3.向客户端公开该接口
您为服务实现该接口后,就须要向客户端公开该接口,以便客户端进行绑定。 要为您的服务公开该接口,请扩展 Service并实现 onBind(),以返回一个类实例,这个类实现了生成的 Stub(见前文所述)。如下是一个向客户端公开 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
}
};
}复制代码
如今,当客户端(如 Activity)调用 bindService() 以链接此服务时,客户端的 onServiceConnected()回调会接收服务的 onBind()方法返回的 mBinder实例。
客户端还必须具备对 interface 类的访问权限,所以若是客户端和服务在不一样的应用内,则客户端的应用 src/目录内必须包含 .aidl文件(它生成 android.os.Binder接口 — 为客户端提供对 AIDL 方法的访问权限)的副本。
当客户端在 onServiceConnected()回调中收到 IBinder时,它必须调用 YourServiceInterface.Stub.asInterface(service)
以将返回的参数转换成 YourServiceInterface
类型。例如:
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;
}
};复制代码
如需查看更多示例代码,请参见 ApiDemos 中的 RemoteService.java 类。
经过 IPC 接口把某个类从一个进程发送到另外一个进程是能够实现的。 不过,您必须确保该类的代码对 IPC 通道的另外一端可用,而且该类必须支持Parcelable 接口。支持 Parcelable接口很重要,由于 Android 系统可经过它将对象分解成可编组到各进程的原语。
如需建立支持 Parcelable协议的类,您必须执行如下操做:
AIDL 在它生成的代码中使用这些方法和字段将您的对象编组和取消编组。
例如,如下这个 Rect.aidl文件可建立一个可打包的 Rect类:
package android.graphics;
// Declare Rect so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable Rect;复制代码
如下示例展现了 Rect类如何实现 Parcelable协议。
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 = newParcelable.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();
}
}复制代码
Rect
类中的编组至关简单。看一看 Parcel上的其余方法,了解您能够向 Parcel 写入哪些其余类型的值。
警告:别忘记从其余进程接收数据的安全影响。 在本例中,Rect从 Parcel读取四个数字,但要由您来确保不管调用方目的为什么这些数字都在相应的可接受值范围内。
调用类必须执行如下步骤,才能调用使用 AIDL 定义的远程接口:
有关调用 IPC 服务的几点说明:
如需了解有关绑定到服务的详细信息,请阅读绑定服务文档。
如下这些示例代码摘自 ApiDemos 项目的远程服务示例代码,展现了如何调用 AIDL 建立的服务。
public static class Binding extends Activity {
/** The primary interface we will be calling on the service. */
IRemoteService mService = null;
/** Another interface we use on the service. */
ISecondary mSecondaryService = null;
Button mKillButton;
TextView mCallbackText;
private boolean mIsBound;
/** * Standard initialization of this activity. Set up the UI, then wait * for the user to poke it before doing anything. */
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.remote_service_binding);
// Watch for button clicks.
Button button = (Button)findViewById(R.id.bind);
button.setOnClickListener(mBindListener);
button = (Button)findViewById(R.id.unbind);
button.setOnClickListener(mUnbindListener);
mKillButton = (Button)findViewById(R.id.kill); mKillButton.setOnClickListener(mKillListener);
mKillButton.setEnabled(false);
mCallbackText = (TextView)findViewById(R.id.callback);
mCallbackText.setText("Not attached.");
}
/** * Class for interacting with the main interface of the service. */
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. We are communicating with our
// service through an IDL interface, so get a client-side
// representation of that from the raw service object.
mService = IRemoteService.Stub.asInterface(service);
mKillButton.setEnabled(true);
mCallbackText.setText("Attached.");
// We want to monitor the service for as long as we are
// connected to it.
try {
mService.registerCallback(mCallback);
} catch (RemoteException e) {
// In this case the service has crashed before we could even
// do anything with it; we can count on soon being
// disconnected (and then reconnected if it can be restarted)
// so there is no need to do anything here.
}
// As part of the sample, tell the user what happened.
Toast.makeText(Binding.this, R.string.remote_service_connected,
Toast.LENGTH_SHORT).show();
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
mKillButton.setEnabled(false);
mCallbackText.setText("Disconnected.");
// As part of the sample, tell the user what happened. Toast.makeText(Binding.this, R.string.remote_service_disconnected,
Toast.LENGTH_SHORT).show();
}
};
/** * Class for interacting with the secondary interface of the service. */
private ServiceConnection mSecondaryConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
// Connecting to a secondary interface is the same as any
// other interface.
mSecondaryService = ISecondary.Stub.asInterface(service);
mKillButton.setEnabled(true);
}
public void onServiceDisconnected(ComponentName className) {
mSecondaryService = null;
mKillButton.setEnabled(false);
}
};
private OnClickListener mBindListener = new OnClickListener() {
public void onClick(View v) {
// Establish a couple connections with the service, binding
// by interface names. This allows other applications to be
// installed that replace the remote service by implementing
// the same interface.
Intent intent = new Intent(Binding.this, RemoteService.class);
intent.setAction(IRemoteService.class.getName());
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
intent.setAction(ISecondary.class.getName());
bindService(intent, mSecondaryConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
mCallbackText.setText("Binding.");
}
};
private OnClickListener mUnbindListener = new OnClickListener() {
public void onClick(View v) {
if (mIsBound) {
// If we have received the service, and hence registered with
// it, then now is the time to unregister.
if (mService != null) {
try {
mService.unregisterCallback(mCallback);
} catch (RemoteException e) {
// There is nothing special we need to do if the service
// has crashed.
}
}
// Detach our existing connection.
unbindService(mConnection);
unbindService(mSecondaryConnection);
mKillButton.setEnabled(false);
mIsBound = false;
mCallbackText.setText("Unbinding.");
}
}
};
private OnClickListener mKillListener = new OnClickListener() {
public void onClick(View v) {
// To kill the process hosting our service, we need to know its
// PID. Conveniently our service has a call that will return
// to us that information.
if (mSecondaryService != null) {
try {
int pid = mSecondaryService.getPid();
// Note that, though this API allows us to request to
// kill any process based on its PID, the kernel will
// still impose standard restrictions on which PIDs you
// are actually able to kill. Typically this means only
// the process running your application and any additional
// processes created by that app as shown here; packages
// sharing a common UID will also be able to kill each
// other's processes.
Process.killProcess(pid);
mCallbackText.setText("Killed service process.");
} catch (RemoteException ex) {
// Recover gracefully from the process hosting the
// server dying.
// Just for purposes of the sample, put up a notification.
Toast.makeText(Binding.this, R.string.remote_call_failed,
Toast.LENGTH_SHORT).show();
}
}
}
};
// ---------------------------------------------------------------------- // Code showing how to deal with callbacks.
// ----------------------------------------------------------------------
/** * This implementation is used to receive callbacks from the remote * service. */
private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {
/** * This is called by the remote service regularly to tell us about * new values. Note that IPC calls are dispatched through a thread * pool running in each process, so the code executing here will * NOT be running in our main thread like most other things -- so, * to update the UI, we need to use a Handler to hop over there. */
public void valueChanged(int value) { mHandler.sendMessage(mHandler.obtainMessage(BUMP_MSG, value, 0));
}
};
private static final int BUMP_MSG = 1;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case BUMP_MSG:
mCallbackText.setText("Received from service: " + msg.arg1);
break;
default:
super.handleMessage(msg);
}
}
};
}复制代码