Aidl和另外的IDL语言一样在一起工作。它允许你定义程序接口在客户端和服务为了可以和其他交互通过进程间的通讯。在android,一个进程不能访问另外一个进程的内存。这样说,他们需要分解他们的对象在基本的实体这个可以让系统理解,并且执行对象访问你的边界。这个代码执行需要很冗余,所以android提供给你用AIDL的方式。
注意:使用AIDL 是需要的只有在你允许客户端在不同的应用去访问你的服务通过IPC并且希望执行多线程在你的服务中。假如你做这些不需要执行IPC访问不同的应用你应该创建你的接口用实现一个Binder或者假如你想执行IPC,但是不需要多线程,实现接口用Messenger。除非你确定你熟悉绑定服务在实现一个AIDL前。
在你开始设计你的AIDL接口时,需要注意调用一个AIDL接口直接的方法调用。你不应该假设关于关于这调用发送的线程。发生什么事不同的根据是在本地进程还是远程进程中的线程。特别是:
1. 调用时本地进程执行在同一一个线程被调用。假如这个你主线程,这个线程继续执行在你的AIDL接口。假如是另外一个线程,这个执行你的代码在服务中。因此假如本地线程访问服务你不需要控制这个线程去执行它(但是假如这种情况,你不需要使用AIDL,用创建一个Binder实现接口代替)
2. 调用一个远程进程在分发从一个线程池中平台保持在你的进程中。你必须准备引进未知线程调用,和多次调用发送在同一时间。换句话,这个AIDL接口的实现需要线程安全。
3. Oneway关键字修改远程调用的行为。当使用,一个远程调用不会被阻塞;它简单的发送事务数据和马上返回。这个实现接口最后接收一个规则的调用从Binder线程池中作为一个一般的远程调用。假如oneway被使用在一个本地调用,这个没有影响并且调用仍然是异步的。
定义一个AIDL接口
你必须顶一个你的AIDL接口在一个.aidl文件中使用java程序语言语法,然后保存它在资源的代码中,应用持有服务和任何的其他应用都可以绑定服务。
当你建立每个应用包含这个.aidl文件,这个androidsdk工具产生一个IBinder接口基于.aidl文件和保存在工程的gen目录下。这个服务必须实现IBinder接口。这个客户端应用可以绑定服务调用方法从这个IBinder执行IPC。
创建一个有界的服务使用AIDL,以下几个步骤:
1. 创建一个.aidl文件:这个文件定义了程序的接口和方法的特征。
2. 实现这个接口:android Sdk工具产生这个接口用java语言,基于你的.aidl文件。这个接口有个内部抽象的名字stub继承了Binder并且实现了aidl接口中的方法。你必须继承stub类并实现方法。
3. 公开这个接口给客户端:实现一个service和覆盖onBind返回一个实现了的Stub类。
注意:任何一个改变在你的AIDL接口在你第一个次发布必须保留兼容为了避免破坏另外的应用使用你的服务。这个因为你的.aidl文件必须被拷贝到另外一个应用为了它们能访问你的服务接口,你必须保留支持原来的接口。
1. 创建一个aidl文件
Aidl使用一个简单的语法让你声明一个接口用一个或多个方法带有参数并返回值。这个参数和返回值可以是任意的类型。甚至是另外aidl产生的接口。
你必须构建一个.aidl文件使用java程序语言。每个.aidl文件必须定义一个接口并且请求这个接口声明和方法。
默认的aidl支持下面的几种类型:
*所有的基础类型在java语言中。
*String
*CharSequence
*List:所有的元素在List中必须支持数据类型在这些列表中的一个AIDL产生的接口和包裹你定义的。一个list也许可以使用泛型类(比如List<String>)实际的类总是接收一个ArrayList,虽然方法被参数使用List接口。
*Map:所有的元素在Map中必须支持数据类型在这些列表中的一个AIDL产生的接口和包裹。
你必须包含所有没有被列出来的类型import语言,即使他们定义在同一个包里。
当你定义你的服务接口,注意:
*方法可以带零个或多个参数,返回一个值或一个void。
*所有不是基础参数请求方向指定数据的流动,in,out或者inout(看接下来的例子)
基本的in都是默认不用,其他的不行。
注意:你应该限制方向你真实的需要,因为整理参数比较昂贵。
*所有的代码注释在.aidl文件包含产生在IBinder接口(除了注释在import和package语句前)
*只有方法支持:你不能暴露AIDL的静态字段。
这里一个例子.aidl文件:
//IRemoteService.aidl
package com.example.android;
// Declare any non-default types here withimport 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 inAIDL.
*/
void basicTypes(int anInt,long aLong,boolean aBoolean,float aFloat,
double aDouble,String aString);
}
简单的保存你的.aidl文件在你的工程src目录下并且当你建立你的应用,SDK工具会产生一个IBinder接口文件爱你的工程gen目录下。这个产生的文件名字和.aidl文件名字一样,但是后缀是.java。
假如你使用eclipse,这个增量创建产生一个binder类马上。假如你不能使用eclipse,然后你需要一个ant工具产生一个binder类在你建立你的应用,你应该建立你的工作用ant debug(或者 ant release)在你写完这个.aidl文件,所有你代码可以连接产生class。
2. 实现接口
当你建立你的应用,androidsdk工具产生一个java接口名字在你的.aidl文件。这个产生的接口包含了一个名为Stub这个是一个抽象的实现它父亲的接口并且声明所有的方法从.aidl文件中。
注意:stub定义了一下帮助的方法,最主要asInterface方法,这个可以拿到一个IBinder(通常这个一个传递给一个客户端的onServiceconnected回调方法)并且返回一个stub接口。看 Calling an IPC Method 更多的详细描述。
实现一个接口产生从.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的实例,这定义了远程调用接口为这个服务。接下来的步骤,这实例被暴露给客户端让他们可以和服务交互。
这里有一些规则你应该注意实现你的AIDL接口:
*进入调用不能保证被执行在主线程中,你应该思考多线程在开始的时候并且正确的建立安全。
*默认的RPC调用时异步的。假如你知道这个服务需要消耗一些时间完成请求,你需要调用它从activity的主线程,因为这个也许暂停这个应用——你应该经常调用它们在一个独立的线程中。
*不是所有的异常你都抛出给调用者。
暴露接口客户端
这个你必须实现接口你服务,你必须暴露它给客户端所以他们能绑定它,为了暴露这个接口为你的服务,继承service和实现onBind方法返回一个你的类的实例实现产生Stub(这个部分在前面已经讨论过了)。这里有一个例子暴露IRemotService实现接口给客户端:
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实例返回被服务的onBind方法。
这个客户端必须访问到这个接口类,所以假如客户端和服务在不同的应用中,然后客户端的应用必须复制一个.aidl文件在他的src目录下(这个是产生android.os.Binder接口体统客户端访问AIDL方法)。
当客户端接收IBinder在onServiceconnected回调函数,他必须调用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; } };
传递对象通过IPC
假如你有一个类你需要从这个进程传递到另一个进程通过一个IPC接口,你可以做。但是你必须确认代码为你的类是否支持在另一个IPC通道和你的类必须支持Parcelable接口。支持Parcelable接口是很重要的因为它允许android系统分解对象成最基本的东西这些被编写通过程序。
为了创建一个类支持Parcelable协议,你必须做以下的一些事:
1. 使你的类继承Parcelable接口。
2. 实现writeToParcel,这个可以携带当前的状态和写入它到一个Parcel。
3. 增加一个静态的变量叫做CREATOR给你类这个对象实现了Parcelable.Creator接口。
4. 最后创建一个.aidl文件这个描述你的包裹类(作为展示Rect.aidl文件)
假如你使用一个自定义的进程,你不需要假如入aidl文件给你的构建。同样的一个头文件在C 语言中,这个.aidl文件不会被编译。
AIDL使用这些方法和变量在代码中他会产生编码和解码你的对象:
比如这里的Rect.aidl文件:
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(); } }
这些排列在Rect类中简单的例子。这个可以看清另外的方法在Parcel看到他们的各种值你可以写一个包裹类。
警告:不要忘记安全的实现在接收数据从另外的进程中。这个例子中,这个Rect读取是个成员从Parcel,但是他必须确保这个需要在值可接受范围中。看 Securityand Permissions 更多的信息你保证你的应用安全。
调用一个IPC方法
这里几步调用类必须需要调用一个远程的接口定义用AIDL:1. 包含一个.aidl文件在一个工程中src文件中
2. 声明一个实例在IBinder接口(产生基于AIDL)
3. 实现ServiceConnection
4. 调用Context.bindService(),传递你ServiceConnection实现。
5. 在你实现的onServiceConnected方法中,你可以接收一个IBinder实例。调用YourInterfaceName.Stub.asInterface抛出返回参数YourInterface类型。
6. 调用方法你定义在接口中。你应该总是需要捕获DeadObjectException异常,整你当连接断链后会被抛出;这个是唯一的一个异常会被远程方法抛出。
7. 断掉连接,调用Context.unbindService()用接口实例。
一些注释在调用一个IPC服务:
*对象引用访问进程
*你可以发送匿名对象作为方法的参数。
这里有一些简单的代码演示一个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. bindService(new Intent(IRemoteService.class.getName()), mConnection, Context.BIND_AUTO_CREATE); bindService(new Intent(ISecondary.class.getName()), 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); } } }; }