Android的接口定义语言(AIDL)
在该文献
定义一个AIDL接口
创建.aidl文件
实现接口
公开接口给客户
传递对象过IPC
调用一个IPC方法
也可以看看
绑定服务
AIDL(Android界面定义语言)相似,你可能已经使用过其他的IDL。它允许您定义的编程接口,客户端和服务,才能与对方使用进程间通信(IPC)进行通信商定。在Android上,一个进程无法正常访问另一个进程的内存。所以讲,他们需要自己的对象分解成原语操作系统能够理解,马歇尔跨边界的对象为您服务。要做到这一点编组的代码是繁琐的编写,因此Android与AIDL为您代劳。
注意:使用AIDL是必要的前提是你让来自不同应用程序的客户端访问服务的IPC,并希望在处理你的服务多线程。如果你不需要在不同的应用程序执行并发IPC,你应该通过实现一个活页夹创建界面,或者如果要执行IPC,但并不需要处理多线程,实现使用信使你的接口。无论如何,要确保你实施一个AIDL之前了解绑定服务。
在你开始设计AIDL接口,要知道是直接的函数调用,调用一个AIDL接口。你不应该对其中调用发生线程的假设。根据呼叫是否来自本地进程中的线程或远程进程会发生什么事是不同的。特别:
从本地工艺制成的呼叫在正在呼叫的同一个线程执行。如果这是你的主UI线程,该线程将继续在AIDL接口来执行。如果它是另一个线程,即,在服务执行代码中的之一。因此,如果只是本地线程访问该服务,可以完全哪些线程中执行它(但如果是这样的话,那么你不应该在所有使用AIDL控制,但通过实施活页夹应改为创建接口)。
从远程进程调用从一个线程池的平台,自己的过程内保持调度。必须从未知线程来电,具有多个呼叫同时发生来制备。换句话说,一个AIDL接口的实现必须完全线程安全。
该单向关键字改变远程调用的行为。在使用时,一个远程调用不会阻塞;它只是发送的交易数据,并立即返回。的接口的实现最终接收该从活页夹线程池作为正常远程呼叫的常规呼叫。如果单向与本地电话使用的,不存在影响和通话仍然是同步的。
定义一个AIDL接口
您必须确定您的AIDL接口使用Java编程语言的语法的.aidl文件,然后将其保存在源代码(在src /目录下)这两个应用托管服务,并结合该服务的任何其他应用程序。
当您生成包含.aidl文件中的每个应用程序的Android SDK工具生成基于该.aidl文件一个IBinder接口,并将其保存到项目的根/目录下。该服务必须实现的IBinder接口为宜。然后,客户端应用程序绑定到从的IBinder服务和呼叫的方法来执行IPC。
要使用AIDL有界服务,请按照下列步骤操作:
创建.aidl文件
该文件定义了方法签名的编程接口。
实现接口
在Android SDK工具生成的Java编程语言的界面,根据您的.aidl文件。该接口具有延伸粘结剂并实现从你的AIDL接口方法的内部抽象类名为存根。您必须扩展stub类和实现的方法。
公开接口给客户
实现服务和覆盖onBind()返回的stub类的实现。
注意:您对您的AIDL接口的先放后的任何修改必须保留,以避免破坏使用你的服务的其他应用程序向后兼容。也就是说,因为你.aidl文件必须为了他们访问您的服务的接口,你必须保持原有的接口支持被复制到其他应用程序。
1.创建.aidl文件
AIDL使用一个简单的语法,它允许您声明一个接口,可以采取参数和返回值的一种或多种方法。的参数和返回值可以是任何类型的,即使是其他的AIDL生成接口。
必须建立基于Java编程语言的.aidl文件。每个.aidl文件必须定义一个接口,只需要接口声明和方法签名。
默认情况下,AIDL支持以下数据类型:
所有原始类型的Java编程语言(如int,长,焦炭,布尔,等等)
串
为CharSequence
名单
列表中的所有元素都必须在此列表中支持的数据类型之一,或者您已经声明了其他AIDL生成的接口或Parcelables并之一。一个List可以作为一个“通用”类(例如,名单<字符串>)。认为对方收到实际的具体类始终是一个ArrayList,虽然会产生使用List接口中的方法。
地图
在地图的所有元素都必须在此列表中支持的数据类型之一,或者您已经声明了其他AIDL生成的接口或Parcelables并之一。通用的地图(如表格地图<字符串,整数>的不支持。认为对方收到实际的具体类始终是一个HashMap,虽然会产生使用Map接口的方法。
您必须包括以上未列出的每个其他类型的导入语句,即使他们在同一个包为你的接口定义。
在定义服务接口,请注意:
方法可以采取零个或多个参数,并返回一个值或空隙。
所有非基本参数要求定向标记指示数据去哪个方向。无论是在,out或inout(见下面的例子)。
原语是在默认情况下,不能以其他方式。
注意:您应该限制的方向是什么真正需要的,因为编组参数是昂贵的。
列入.aidl文件中的所有代码的注释都包含在生成的IBinder接口(除进口和包语句之前注释)。
只有方法的支持;你不能揭露AIDL静态字段。
下面是一个例子.aidl文件:
// IRemoteService.aidl
package 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工具生成在项目的根/目录下的IBinder接口文件。所生成的文件名相匹配的.aidl文件名,但具有一个.java扩展名(例如,IRemoteService.aidl导致IRemoteService.java)。
如果你使用Eclipse,增量构建几乎立即产生粘合剂类。如果你不使用Eclipse,然后在Ant生成工具构建粘合剂类下一次你的应用程序,你应该尽快建立与调试蚂蚁(或蚂蚁释放)项目为你写完.aidl文件,这样你的代码可以对链接生成的类。
2.实现接口
当你建立你的应用程序时,Android SDK工具生成您.aidl文件命名的一个.java接口文件。生成的接口包括一个名为Stub子类是一个抽象的实现其父接口(例如,YourInterface.Stub),并声明所有从.aidl文件的方法。
注:存根还定义了一些辅助方法,最值得注意的是asInterface(),它接受一个IBinder(通常是一个传递给客户的onServiceConnected()回调方法)并返回存根接口的一个实例。请参见调用一个IPC方法如何使这个转换的更多细节。
为了实现从.aidl产生的界面,延长所生成的粘合剂的接口(例如,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是存根类(活页夹),它定义该服务的RPC接口的一个实例。在下一步骤中,这种情况下被暴露于客户端,这样他们可以与服务进行交互。
还有,你应该知道你的实现AIDL接口时,一些规则:
呼入电话无法保证会在主线程上执行的,所以你需要考虑从一开始多线程和正确建立服务是线程安全的。
默认情况下,RPC调用是同步的。如果您知道该服务需要超过几毫秒的时间来完成一个请求,你不应该从活动的主线程中调用它,因为它可能会挂起应用(Android版可能会显示一个“应用程序没有响应”对话框) - 你应该通常在客户端一个单独的线程调用它们。
没有你抛出异常发送回调用者。
3.公开接口给客户
一旦你实现的接口为您服务,您需要将其暴露给客户,使他们能够绑定到它。揭露为您服务的接口,扩展服务并实现onBind()返回类实现生成的存根(如上一节中讨论)的一个实例。下面是公开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
}
};
}
现在,当一个客户端(如活动)调用bindService()来连接到该服务,客户端的onServiceConnected()回调接收该服务的onBind()方法返回的mBinder实例。
客户端还必须能够访问的接口类,因此,如果在客户端和服务都在独立的应用程序,则在客户端的应用程序必须在其的src /目录的.aidl文件的副本(它产生android.os.Binder接口 - 提供给AIDL方法,客户端访问)。
当客户端收到的onServiceConnected()回调的IBinder,它必须调用YourServiceInterface.Stub.asInterface(服务)返回的参数强制转换为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接口从一个进程发送到另一个类,你可以做到这一点。但是,你必须确保你的类的代码可到IPC通道的另一边,你的类必须支持Parcelable接口。支持Parcelable接口很重要,因为它可以让Android系统分解物进入,可以跨进程编组元。
要创建支持Parcelable协议的类时,必须做到以下几点:
让你的类实现Parcelable接口。
实施writeToParcel,这需要对象的当前状态,并把它写入到一个包。
一个名为CREATOR静态字段添加到您的类,这是实现Parcelable.Creator接口的对象。
最后,创建声明你parcelable类(如图所示为Rect.aidl文件,下同).aidl文件。
如果您使用的是自定义生成过程中,不要将.aidl文件添加到您的构建。类似于C语言的头文件,该文件.aidl未编译。
AIDL使用这些方法和字段在它生成于编组和解组的对象的代码。
例如,这里是一个Rect.aidl文件来创建一个矩形类的parcelable:
package android.graphics;
// Declare Rect so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable 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 = 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();
}
}
在矩形类的编组是非常简单的。看看包裹上的其他方法来看到其他类型的值,你可以写一个包裹。
警告:不要忘记其他进程接收数据的安全问题。在这种情况下,矩形读出从包裹四个数字,但它是由你来确保,这些是任何呼叫者正试图做的值的可接受的范围内。有关如何保证应用程序的安全免受恶意软件的更多信息,请参阅安全和权限。
调用一个IPC方法
这里有一个调用的类必须调用与AIDL定义的远程接口的步骤:
包括在项目的src /目录下的文件.aidl。
声明的IBinder接口(生成基于所述AIDL)的一个实例。
实施ServiceConnection。
呼叫Context.bindService(),并传入您的ServiceConnection实现。
在您的实现onServiceConnected(),你会收到一个IBinder实例(称为服务)。呼叫YourInterfaceName.Stub.asInterface((的IBinder)服务)将返回的参数强制转换为YourInterface类型。
请致电您在接口上定义的方法。你应该总是陷阱DeadObjectException异常,当连接中断而抛出;这将是由远程方法抛出的唯一例外。
要断开连接,调用Context.unbindService()与接口的实例。
在调用一个IPC服务需要注意几点:
对象引用跨进程计数。
您可以发送匿名对象作为方法的参数。
有关绑定到服务的更多信息,请阅读绑定服务文档。
下面是一些示例代码演示调用一个AIDL创建的服务,从项目ApiDemos远程服务取试样。
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);
}
}
};
}