Android的AIDL机制Word格式.docx
《Android的AIDL机制Word格式.docx》由会员分享,可在线阅读,更多相关《Android的AIDL机制Word格式.docx(18页珍藏版)》请在冰豆网上搜索。
使用该关键字时,远程调用不会阻塞;
它只是发送事务数据并立即返回。
接口的实现最终接收此调用时,是以正常远程调用形式将其作为来自Binder线程池的常规调用进行接收。
如果oneway用于本地调用,则不会有任何影响,调用仍是同步调用。
定义AIDL接口
您必须使用Java编程语言语法在.aidl文件中定义AIDL接口,然后将它保存在托管服务的应用以及任何其他绑定到服务的应用的源代码(src/目录)内。
您开发每个包含.aidl文件的应用时,AndroidSDK工具都会生成一个基于该.aidl文件的IBinder接口,并将其保存在项目的gen/目录中。
服务必须视情况实现IBinder接口。
然后客户端应用便可绑定到该服务,并调用IBinder中的方法来执行IPC。
如需使用AIDL创建绑定服务,请执行以下步骤:
创建.aidl文件
此文件定义带有方法签名的编程接口。
实现接口
AndroidSDK工具基于您的.aidl文件,使用Java编程语言生成一个接口。
此接口具有一个名为Stub的内部抽象类,用于扩展Binder类并实现AIDL接口中的方法。
您必须扩展Stub类并实现方法。
向客户端公开该接口
实现Service并重写onBind()以返回Stub类的实现。
注意:
在AIDL接口首次发布后对其进行的任何更改都必须保持向后兼容性,以避免中断其他应用对您的服务的使用。
也就是说,因为必须将您的.aidl文件复制到其他应用,才能让这些应用访问您的服务的接口,因此您必须保留对原始接口的支持。
创建.aidl文件
AIDL使用简单语法,使您能通过可带参数和返回值的一个或多个方法来声明接口。
参数和返回值可以是任意类型,甚至可以是其他AIDL生成的接口。
您必须使用Java编程语言构建.aidl文件。
每个.aidl文件都必须定义单个接口,并且只需包含接口声明和方法签名。
默认情况下,AIDL支持下列数据类型:
Java编程语言中的所有原语类型(如int、long、char、boolean等等)
String
CharSequence
List
List中的所有元素都必须是以上列表中支持的数据类型、其他AIDL生成的接口或您声明的可打包类型。
可选择将List用作“通用”类(例如,List)。
另一端实际接收的具体类始终是ArrayList,但生成的方法使用的是List接口。
Map
Map中的所有元素都必须是以上列表中支持的数据类型、其他AIDL生成的接口或您声明的可打包类型。
不支持通用Map(如Map
定义服务接口时,请注意:
方法可带零个或多个参数,返回值或空值。
所有非原语参数都需要指示数据走向的方向标记。
可以是in、out或inout(见以下示例)。
原语默认为in,不能是其他方向。
您应该将方向限定为真正需要的方向,因为编组参数的开销极大。
.aidl文件中包括的所有代码注释都包含在生成的IBinder接口中(import和package语句之前的注释除外)
只支持方法;
您不能公开AIDL中的静态字段。
以下是一个.aidl文件示例:
//IRemoteService.aidl
packagecom.example.android;
//Declareanynon-defaulttypesherewithimportstatements
/**Exampleserviceinterface*/
interfaceIRemoteService{
/**RequesttheprocessIDofthisservice,todoevilthingswithit.*/
intgetPid();
/**Demonstratessomebasictypesthatyoucanuseasparameters
*andreturnvaluesinAIDL.
*/
voidbasicTypes(intanInt,longaLong,booleanaBoolean,floataFloat,
doubleaDouble,StringaString);
}
只需将您的.aidl文件保存在项目的src/目录内,当您开发应用时,SDK工具会在项目的gen/目录中生成IBinder接口文件。
生成的文件名与.aidl文件名一致,只是使用了.java扩展名(例如,IRemoteService.aidl生成的文件名是IRemoteService.java)。
如果您使用AndroidStudio,增量编译几乎会立即生成Binder类。
如果您不使用AndroidStudio,则Gradle工具会在您下一次开发应用时生成Binder类—您应该在编写完.aidl文件后立即用gradleassembleDebug(或gradleassembleRelease)编译项目,以便您的代码能够链接到生成的类。
2.实现接口
当您开发应用时,AndroidSDK工具会生成一个以.aidl文件命名的.java接口文件。
生成的接口包括一个名为Stub的子类,这个子类是其父接口(例如,YourInterface.Stub)的抽象实现,用于声明.aidl文件中的所有方法。
Stub还定义了几个帮助程序方法,其中最引人关注的是asInterface(),该方法带IBinder(通常便是传递给客户端onServiceConnected()回调方法的参数)并返回存根接口实例。
如需了解如何进行这种转换的更多详细信息,请参见调用IPC方法一节。
如需实现.aidl生成的接口,请扩展生成的Binder接口(例如,YourInterface.Stub)并实现从.aidl文件继承的方法。
以下是一个使用匿名实例实现名为IRemoteService的接口(由以上IRemoteService.aidl示例定义)的示例:
privatefinalIRemoteService.StubmBinder=newIRemoteService.Stub(){
publicintgetPid(){
returnProcess.myPid();
}
publicvoidbasicTypes(intanInt,longaLong,booleanaBoolean,
floataFloat,doubleaDouble,StringaString){
//Doesnothing
};
现在,mBinder是Stub类的一个实例(一个Binder),用于定义服务的RPC接口。
在下一步中,将向客户端公开该实例,以便客户端能与服务进行交互。
在实现AIDL接口时应注意遵守以下这几个规则:
由于不能保证在主线程上执行传入调用,因此您一开始就需要做好多线程处理准备,并将您的服务正确地编译为线程安全服务。
默认情况下,RPC调用是同步调用。
如果您明知服务完成请求的时间不止几毫秒,就不应该从Activity的主线程调用服务,因为这样做可能会使应用挂起(Android可能会显示“ApplicationisNotResponding”对话框)—您通常应该从客户端内的单独线程调用服务。
您引发的任何异常都不会回传给调用方。
3.向客户端公开该接口
您为服务实现该接口后,就需要向客户端公开该接口,以便客户端进行绑定。
要为您的服务公开该接口,请扩展Service并实现onBind(),以返回一个类实例,这个类实现了生成的Stub(见前文所述)。
以下是一个向客户端公开IRemoteService示例接口的服务示例。
publicclassRemoteServiceextendsService{
@Override
publicvoidonCreate(){
super.onCreate();
publicIBinderonBind(Intentintent){
//Returntheinterface
returnmBinder;
privatefinalIRemoteService.StubmBinder=newIRemoteService.Stub(){
};
现在,当客户端(如Activity)调用bindService()以连接此服务时,客户端的onServiceConnected()回调会接收服务的onBind()方法返回的mBinder实例。
客户端还必须具有对interface类的访问权限,因此如果客户端和服务在不同的应用内,则客户端的应用src/目录内必须包含.aidl文件(它生成android.os.Binder接口—为客户端提供对AIDL方法的访问权限)的副本。
当客户端在onServiceConnected()回调中收到IBinder时,它必须调用
YourServiceInterface.Stub.asInterface(service)以将返回的参数转换成YourServiceInterface类型。
例如:
IRemoteServicemIRemoteService;
privateServiceConnectionmConnection=newServiceConnection(){
//Calledwhentheconnectionwiththeserviceisestablished
publicvoidonServiceConnected(ComponentNameclassName,IBinderservice){
//FollowingtheexampleaboveforanAIDLinterface,
//thisgetsaninstanceoftheIRemoteInterface,whichwecanusetocallontheservice
mIRemoteService=IRemoteService.Stub.asInterface(service);
//Calledwhentheconnectionwiththeservicedisconnectsunexpectedly
publicvoidonServiceDisconnected(ComponentNameclassName){
Log.e(TAG,"
Servicehasunexpectedlydisconnected"
);
mIRemoteService=null;
如需查看更多示例代码,请参见ApiDemos中的RemoteService.java类。
通过IPC传递对象
通过IPC接口把某个类从一个进程发送到另一个进程是可以实现的。
不过,您必须确保该类的代码对IPC通道的另一端可用,并且该类必须支持Parcelable接口。
支持Parcelable接口很重要,因为Android系统可通过它将对象分解成可编组到各进程的原语。
如需创建支持Parcelable协议的类,您必须执行以下操作:
让您的类实现Parcelable接口。
实现writeToParcel,它会获取对象的当前状态并将其写入Parcel。
为您的类添加一个名为CREATOR的静态字段,这个字段是一个实现Parcelable.Creator接口的对象。
最后,创建一个声明可打包类的.aidl文件(按照下文Rect.aidl文件所示步骤)。
如果您使用的是自定义编译进程,切勿在您的编译中添加.aidl文件。
此.aidl文件与C语言中的头文件类似,并未编译。
AIDL在它生成的代码中使用这些方法和字段将您的对象编组和取消编组。
例如,以下这个Rect.aidl文件可创建一个可打包的Rect类:
packageandroid.graphics;
//DeclareRectsoAIDLcanfinditandknowsthatitimplements
//theparcelableprotocol.
parcelableRect;
以下示例展示了Rect类如何实现Parcelable协议。
importandroid.os.Parcel;
importandroid.os.Parcelable;
publicfinalclassRectimplementsParcelable{
publicintleft;
publicinttop;
publicintright;
publicintbottom;
publicstaticfinalParcelable.Creator<
Rect>
CREATOR=new
Parcelable.Creator<
(){
publicRectcreateFromParcel(Parcelin){
returnnewRect(in);
publicRect[]newArray(intsize){
returnnewRect[size];
publicRect(){
privateRect(Parcelin){
readFromParcel(in);
publicvoidwriteToParcel(Parcelout){
out.writeInt(left);
out.writeInt(top);
out.writeInt(right);
out.writeInt(bottom);
publicvoidreadFromParcel(Parcelin){
left=in.readInt();
top=in.readInt();
right=in.readInt();
bottom=in.readInt();
Rect类中的编组相当简单。
看一看Parcel上的其他方法,了解您可以向Parcel写入哪些其他类型的值。
警告:
别忘记从其他进程接收数据的安全影响。
在本例中,Rect从Parcel读取四个数字,但要由您来确保无论调用方目的为何这些数字都在相应的可接受值范围内。
如需了解有关如何防止应用受到恶意软件侵害、保证应用安全的更多信息,请参见安全与权限。
调用IPC方法
调用类必须执行以下步骤,才能调用使用AIDL定义的远程接口:
在项目src/目录中加入.aidl文件。
声明一个IBinder接口实例(基于AIDL生成)。
实现ServiceConnection。
调用{@linkandroid.content.Context#bindService(android.content.Intent,android.content.ServiceConnection,int)Context.bindService()},以传入您的ServiceConnection实现。
在您的onServiceConnected()实现中,您将收到一个IBinder实例(名为service)。
调用YourInterfaceName.Stub.asInterface((IBinder)service),以将返回的参数转换为YourInterface类型。
调用您在接口上定义的方法。
您应该始终捕获DeadObjectException异常,它们是在连接中断时引发的;
这将是远程方法引发的唯一异常。
如需断开连接,请使用您的接口实例调用{@linkandroid.content.Context#unbindService(android.content.ServiceConnection)Context.unbindService()}。
有关调用IPC服务的几点说明:
对象是跨进程计数的引用。
您可以将匿名对象作为方法参数发送。
如需了解有关绑定到服务的详细信息,请阅读绑定服务文档。
以下这些示例代码摘自ApiDemos项目的远程服务示例代码,展示了如何调用AIDL创建的服务。
publicstaticclassBindingextendsActivity{
/**Theprimaryinterfacewewillbecallingontheservice.*/
IRemoteServicemService=null;
/**Anotherinterfaceweuseontheservice.*/
ISecondarymSecondaryService=null;
ButtonmKillButton;
TextViewmCallbackText;
privatebooleanmIsBound;
/**
*Standardinitializationofthisactivity.SetuptheUI,thenwait
*fortheusertopokeitbeforedoinganything.
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.remote_service_binding);
//Watchforbuttonclicks.
Buttonbutton=(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("
Notattached."
*Classforinteractingwiththemaininterfaceoftheservice.
privateServiceConnectionmConnection=newServiceConnection(){
publicvoidonServiceConnected(ComponentNameclassName,
IBinderservice){
//Thisiscalledwhentheconnectionwiththeservicehasbeen
//established,givingustheserviceobjectwecanuseto
//interactwiththeservice.Wearecommunicatingwithour
//servicethroughanIDLinterface,sogetaclient-side
//representationofthatfromtherawserviceobject.
mService=IRemoteService.Stub.asInterface(service);
mKillButton.setEnabled(true);
Attached."
)