AIDL(Android界面定义语言)相似,你可能已经使用过其他的IDL。它允许您定义的编程接口,客户端和服务,才能与对方使用进程间通信(IPC)进行通信商定。在Android上,一个进程无法正常访问另一个进程的内存。所以讲,他们需要自己的对象分解成原语操作系统能够理解,马歇尔跨边界的对象为您服务。要做到这一点编组的代码是繁琐的编写,因此Android与AIDL为您代劳。
注意:使用AIDL是必要的前提是你让来自不同应用程序的客户端访问服务的IPC,并希望在处理你的服务多线程。如果你不需要在不同的应用程序执行并发IPC,你应该创建界面实现活页夹,或者,如果要执行IPC,但并不需要处理多线程,实现你的界面使用Messenger的。无论如何,请确保您了解绑定服务实现一个AIDL之前。
在你开始设计AIDL接口,要知道是直接的函数调用,调用一个AIDL接口。你不应该对其中调用发生线程的假设。根据呼叫是否来自本地进程中的线程或远程进程会发生什么事是不同的。特别:
单向
关键字改变远程调用的行为。在使用时,一个远程调用不会阻塞; 它只是发送的交易数据,并立即返回。的接口的实现最终接收该从常规呼叫粘合剂
线程池作为正常远程调用。如果单向
与本地电话使用的,不存在影响和通话仍然是同步的。您必须确定您的AIDL接口在.aidl
使用Java编程语言的语法文件,然后将其保存在源代码(在SRC /
目录下)这两个应用托管服务,并结合该服务的任何其他应用程序。
当你建立一个包含每个应用程序.aidl
文件时,Android SDK工具生成的IBinder
基于接口.aidl
文件并将其保存在项目的根/
目录下。该服务必须实现的IBinder
接口为宜。然后,客户端应用程序绑定到从服务和调用方法的IBinder
执行IPC。
要使用AIDL有界服务,请按照下列步骤操作:
该文件定义了方法签名的编程接口。
在Android SDK工具生成的Java编程语言的界面,根据您的 .aidl
文件。这个接口有一个内部抽象类名为存根
扩展 粘结剂
并实现从你的AIDL接口中的方法。您必须扩展 存根
类,并实现方法。
实施服务
,并覆盖onBind()
来回报您的实施存根
类。
注意:您对您的AIDL接口的先放后的任何修改必须保留,以避免破坏使用你的服务的其他应用程序向后兼容。也就是说,因为你.aidl
文件必须为了他们访问您的服务的接口,你必须保持原有的接口支持被复制到其他应用程序。
AIDL使用一个简单的语法,它允许您声明一个接口,可以采取参数和返回值的一种或多种方法。的参数和返回值可以是任何类型的,即使是其他的AIDL生成接口。
您必须构建.aidl
使用Java编程语言文件。每个.aidl
文件必须定义一个接口,只需要接口声明和方法签名。
默认情况下,AIDL支持以下数据类型:
INT
,长
, 焦炭
,布尔
,等等)串
为CharSequence
名单
在所有的元素列表
必须在此列表中支持的数据类型之一,或者您已经声明了其他AIDL生成的接口或Parcelables并之一。一个列表
可以选择作为“通用”类(例如, 名单
)。认为对方收到实际的具体类始终是一个ArrayList中
,虽然会产生使用方法列表
界面。
地图
在所有元素地图
必须在此列表中支持的数据类型之一,或者您已经声明了其他AIDL生成的接口或Parcelables并之一。通用图,(如那些形式的 地图<字符串,整数>
不被支持。认为对方收到实际的具体类始终是一个HashMap中
,虽然会产生使用方法地图
界面。
您必须包括进口
以上未列出的其他每个类型的语句,即使他们在同一个包为你的接口定义。
在定义服务接口,请注意:
在
,出
,或INOUT
(见下面的例子)。 原语是在
默认情况下,不能以其他方式。
注意:您应该限制的方向是什么真正需要的,因为编组参数是昂贵的。
.aidl
文件都包含在生成的IBinder
接口(除进口和包语句之前的评论)。下面是一个例子.aidl
文件:
// IRemoteService.aidl 包融为一体。例如,机器人; //此处声明任何非默认类型的import语句 / **示例服务接口* / 接口 IRemoteService { / **要求这项服务的进程ID,做坏事用它。* / INT GETPID (); / **表明可以作为参数使用一些基本类型 。*在AIDL返回值 * / 无效basicTypes (INT ANINT , 长一起, 布尔aBoolean , 漂浮水上, 双aDouble , 字符串ASTRING ) ; }
只需保存.aidl
文件在项目的的src /
目录下,当你建立你的应用程序,SDK工具生成的IBinder
在项目的接口文件根/
目录下。所生成的文件名 相匹配的.aidl
文件名 ,但是具有的.java
扩展名(例如,IRemoteService.aidl
导致IRemoteService.java
)。
如果你使用Android工作室,增量构建几乎立即产生粘合剂类。如果你不使用Android工作室,那么摇篮工具生成构建应用程序,你应该建立与项目粘合剂类下一次gradle这个assembleDebug
(或gradle这个assembleRelease
只要你写完).aidl
文件,所以你的代码可以对链接生成的类。
当你建立你的应用程序时,Android SDK工具生成的.java
您的名字命名的接口文件.aidl
文件。生成的接口包括一个名为子类存根
这是一个抽象实现其父接口(例如,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 ) { //不执行任何操作 } };
现在mBinder
是的一个实例存根
类(粘合剂
),其定义了服务的RPC接口。在下一步骤中,这种情况下被暴露于客户端,这样他们可以与服务进行交互。
还有,你应该知道你的实现AIDL接口时,一些规则:
一旦你实现的接口为您服务,您需要将其暴露给客户,使他们能够绑定到它。揭露为您服务的接口,扩展服务
并实现onBind()
返回类实现所产生的一个实例存根
(如上一节中讨论)。下面是一个公开的例子服务IRemoteService
例如接口给客户。
返回 不执行任何操作 } }; }
现在,当一个客户端(如活动)调用bindService()
来连接到该服务,客户端的onServiceConnected()
回调接收 mBinder
由服务的返回的实例onBind()
方法。
客户端还必须能够访问的接口类,因此,如果在客户端和服务都在独立的应用程序,则在客户端的应用程序必须具有的副本.aidl
在其文件的src /
目录(它产生android.os.Binder
接口-提供给AIDL方法,客户端访问)。
当客户端收到的IBinder
在onServiceConnected()
回调,它必须调用 YourServiceInterface .Stub.asInterface(服务)
将返回参数转换为YourServiceInterface
类型。例如:
IRemoteService mIRemoteService ; 私人 ServiceConnection mConnection = 新 ServiceConnection () { //当与服务建立连接调用 公共 无效onServiceConnected (组件名的className , 的IBinder 服务) { //按照上面一个AIDL接口的例子, //这得 到一个在IRemoteInterface,我们可以用它来 在服务调用的实例 mIRemoteService = IRemoteService 。存根。asInterface (服务) } //调用时与服务的连接意外断开 公共 无效onServiceDisconnected (组件名的className ) { 日志。Ë (TAG , “服务意外中断” ); mIRemoteService = 空; } };
欲了解更多示例代码,请参见RemoteService.java
类ApiDemos。
如果你有,你想通过IPC接口从一个进程发送到另一个类,你可以做到这一点。但是,你必须确保你的类的代码可到IPC通道的另一边,你的类必须支持Parcelable
接口。支持Parcelable
接口很重要,因为它可以让Android系统分解物进入,可以跨进程编组元。
要创建支持类Parcelable
协议,必须做到以下几点:
Parcelable
接口。writeToParcel
,这需要对象的当前状态,并把它写入到一个包
。CREATOR
到您的类,这是实现一个对象Parcelable.Creator
接口。.aidl
声明你parcelable类(如图所示的文件 Rect.aidl
文件,下同)。 如果您使用的是自定义生成过程中,千万不能添加.aidl
文件到您的构建。类似于C语言的头文件,这.aidl
文件不被编译。
AIDL使用这些方法和字段在它生成于编组和解组的对象的代码。
例如,这里是一个Rect.aidl
文件来创建一个矩形
类的parcelable:
包装机器人,图形; //声明矩形这样AIDL可以找到它,知道它实现 //将parcelable协议。 parcelable 矩形;
这里是该怎么一个例子矩形
类实现 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 (); } }
在编组矩形
类是非常简单的。看看在其他的方法包裹
看到其他类型的值,你可以写一个包裹。
警告:不要忘记其他进程接收数据的安全问题。在这种情况下,矩形
读取四个数字包裹
,但它是由你来确保,这些是任何呼叫者正试图做的值的可接受的范围内。见安全和权限有关如何保证应用程序的安全免受恶意软件的更多信息。
这里有一个调用的类必须调用与AIDL定义的远程接口的步骤:
.aidl
在项目文件中的src /
目录下。的IBinder
接口(生成基于所述AIDL)。ServiceConnection
。Context.bindService()
,并传入您的ServiceConnection
实现。onServiceConnected()
,您将收到的IBinder
实例(称为服务
)。呼叫 YourInterfaceName .Stub.asInterface((的IBinder)服务)
将返回的参数强制转换为YourInterface类型。DeadObjectException
异常,当连接中断而抛出; 这将是由远程方法抛出的唯一例外。Context.unbindService()
与接口的实例。在调用一个IPC服务需要注意几点:
有关绑定到服务的更多信息,请阅读绑定服务 文档。
下面是一些示例代码演示调用一个AIDL创建的服务,从项目ApiDemos远程服务取试样。
公共 静态 类 绑定 扩展 活动 { / **主界面,我们将在服务调用进行。* / IRemoteService MSERVICE = 空; / **我们使用该服务的另一个接口。 本次活动的标准初始化。设置UI,然后等待 *为用户做之前,它捅 留意按钮 附“ ); } / ** *分类为与所述主界面交互 这与该服务的连接已被调用时 //建立,使我们的服务对象,我们可以用它来 //使用服务进行交互。我们正在与我们沟通 ,通过IDL接口//服务,因此得到了客户端 //从原始服务的代表 我们想监控,只要我们的服务 //连接到它。 尝试 { MSERVICE 。registerCallback (mCallback ); } 赶上 (RemoteException的ē ) { //在这种情况下,我们可能甚至在服务已经崩溃 //做与任何东西,我们可以指望很快被 //断开(然后重新连接,如果可以重新启动) //所以没有必要做任何事情在这里。 } //由于样本的一部分,告诉用户什么 这就是所谓的当与服务的连接已 //意外断开-也就是说,它的进程崩溃。 MSERVICE = 空; mKillButton 。的setEnabled (假); mCallbackText 。的setText (“断开” ); //作为一部分样本,告诉用户什么 类具有的二级界面进行交互 连接到辅助接口是相同的任何 //其他 与该服务建立了几个连接,结合 由接口名称//这允许其它应用程序是 //装通过实施替换远程服务 //相同 如果我们收到了该服务,因此与注册 //它,那么现在是注销的时间 ,如果 (MSERVICE != 空) { 尝试 { MSERVICE 。unregisterCallback (mCallback ;) } 赶上 (RemoteException的ē ) { //有没什么特别的,我们需要做的,如果服务 //已崩溃。 } } //分离我们现有的 杀死进程托管我们的服务,我们需要知道它 // PID。我们的便利服务,具有呼叫,将返回 //给我们的信息。 如果 (mSecondaryService != 空) { 尝试 { INT PID = mSecondaryService 。GETPID ( ); //需要注意的是,虽然该API允许我们请求 //杀根据它的PID任何进程,内核会 //仍然征收标准限制它的PID你 //居然能够杀死通常,这意味着只。 //运行应用程序和任何其他的过程 由应用程序创建//流程如下图所示;包 //共用一个UID也能自相残杀 。//其他的流程 过程。killProcess (PID ); mCallbackText 。的setText (“杀了服务流程。” ); } 赶上 (RemoteException的前) { //从托管过程中恢复正常 。//服务器垂死 //只为样本的目的,提出了一个 -------------------------------------------------- -------------------- //显示如何处理回调代码。 // ------------------ -------------------------------------------------- - / ** *这种实现用于从远程接收回调 *服务。 * / 私人 IRemoteServiceCallback mCallback = 新 IRemoteServiceCallback 。存根() { / ** *这是由远程服务名为定期向我们讲述 *新。值的注意的是IPC调用都是通过一个线程进行调度 的每个进程中运行*池,所以这里执行的代码将 *不是我们最喜欢的其他东西主线程中运行-因此, *更新UI,我们需要使用一个Handler来跃过 从服务:“ + 味精。ARG1 ); 打破; 默认: 超。的handleMessage (味精); } } }; }