本帖最后由 zzj123 于 2017-6-14 14:39 编辑
AIDL 实现进程间通信
在Android 平台中,各个组件运行在自己的进程中,他们之间是不能相互访问的,但是在程序之间是不可避免的要传递一些对象,在进程之间相互通信。为了实现进程之间的相互通信,Android 采用了一种轻量级的实现方式 RPC(Remote Procedure Call 远程进程调用)来完成进程之间的通信,并且 Android 通过接口定义语言(Android Interface Definition Language ,AIDL)来生成两个进程之间相互访问的代码,例如,你在 Activity 里的代码需要访问 Service 中的一个方法,那么就可以通过这种方式来实现了。
AIDL 是 Android 的一种接口描述语言。编译器可以通过 aidl 文件生成一段代码,通过预先定义的接口达到两个进程内部通信进程的目的. 如果需要在一个 Activity 中, 访问另一个 Service 中的某个对象,需要先将对象转化成 AIDL 可识别的参数(可能是多个参数), 然后使用AIDL 来传递这些参数, 在消息的接收端, 使用这些参数组装成自己需要的对象。
如果想让我们的 Service 可以提供远程服务,那么就必须定义一个.aidl 文件,该文件使用的是 java 语法,类似 java 的接口。然后将该文件在客户端和服务端的 src 目录下各自保存一份,这样编译器就会根据 aidl 文件自动生成一个 java 类,也就说在客户端和服务端都拥有了相同的类文件了。
aidl 的使用分为如下三个步骤(见官方文档):
一、Create the .aidl file
AIDL uses a simple syntaxthat lets youdeclarean interfacewith one or more methods that can take parameters and return values.The parametersandreturn values can be of any type,
29
even otherAIDL-generated interfaces.
You must construct the .aidl file using the Java programming language. Each .aidl filemust definea singleinterface and requires only theinterfacedeclaration andmethod signatures.
By default, AIDLsupports the followingdatatypes:
All primitive types in theJava programminglanguage (suchas int, long, char, boolean, and so on)
String
CharSequence
List
All elements inthe List must beoneofthesupporteddata types inthis list or one of the other AIDL-generated interfacesor parcelables you'vedeclared.A List may optionally be used as a "generic" class (for example, List<String>). The actual concrete class that the other side receives is always an ArrayList, although the method is generated to use the List interface.
Map
All elements inthe Mapmustbe one of the supported data typesin this list oroneof the other AIDL-generated interfaces or parcelables you've declared.Generic maps, (such as those of the formMap<String,Integer> are notsupported. The actualconcrete class that the other side receivesis always a HashMap,although the method is generated to use theMap interface.
You must includean importstatement for each additionaltypenotlisted above,evenif they are defined in thesamepackageas yourinterface.
When definingyourserviceinterface, beaware that:
Methods cantakezeroor more parameters, andreturn avalue orvoid.
All non-primitive parameters require adirectional tagindicating which way the data goes. Eitherin,out,orinout (see the example below).
Primitivesare in by default, and cannotbe otherwise.
Caution: You should limit the direction to what is truly needed, because marshalling parametersisexpensive.
All code comments included in the .aidl fileare included in the generated IBinder interface (except for commentsbefore theimport and packagestatements).
Only methods aresupported; youcannot exposestatic fields inAIDL.
Here is anexample.aidl file: // IRemoteService.aidl package com.example.android;
30
// Declareany non-defaulttypes here with importstatements
/** Example service interface */ interface IRemoteService {
/** RequesttheprocessIDof thisservice, to doevilthings with it. */ int getPid();
/** Demonstratessomebasic types that you can useas parameters *andreturn values in AIDL.
*/
void basicTypes(int anInt,longaLong, boolean aBoolean, floataFloat, doubleaDouble, String aString);
}
Simply save your .aidl file in your project's src/ directory and when you build your application, the SDK tools generate the IBinder interface file in your project's gen/ directory. The generated file name matches the .aidl file name, but with a .java extension (for example, IRemoteService.aidl results in IRemoteService.java).
If you use Eclipse, the incremental build generates the binder class almost immediately. If you do not use Eclipse, then the Ant tool generates the binder class next time you build your application—you should build your project with ant debug (or ant release) as soon as you're finished writing the .aidl file, so that your code can link against the generated class.
二、Implement the interface
When you build yourapplication, the Android SDKtools generate a.java interfacefile named after your .aidl file. The generatedinterfaceincludes a subclass named Stub that is an abstract implementation of its parent interface (for example, YourInterface.Stub) and declares all themethods from the .aidlfile.
To implement the interface generated fromthe .aidl,extend the generatedBinder interface (for example, YourInterface.Stub) and implement the methods inherited from the .aidl file.
Here is anexample implementation of an interface calledIRemoteService(defined bythe IRemoteService.aidl example,above) using ananonymousinstance:
private finalIRemoteService.Stub mBinder = new IRemoteService.Stub() { public intgetPid(){
returnProcess.myPid();
}
public voidbasicTypes(intanInt, long aLong,boolean aBoolean, float aFloat,double aDouble, String aString){
// Does nothing
}
};
31
Now the mBinder is an instance ofthe Stub class (a Binder), which defines the RPC interface for the service. In the next step, thisinstance isexposed to clients so they can interact with the service.
There are a fewrulesyou should be aware ofwhenimplementingyourAIDLinterface:
Incoming calls are not guaranteedto be executed on the main thread, so you need to think about multithreading from the start and properlybuild yourservice tobe thread-safe.
By default,RPCcalls are synchronous. If you knowthat the servicetakes more than a few milliseconds to complete a request, you should not callit from the activity's main thread, becauseitmighthangtheapplication(Android mightdisplayan "Application is
Not Responding" dialog)—you should usually callthem from a separate thread in the client.
No exceptionsthatyou throw are sent back tothecaller.
三、Expose the interface to clients
Once you've implemented the interface for your service, you need toexpose it to clients so they can bind toit.To expose the interface foryour service, extend Service and implement onBind() to return an instance of your class that implements the generated Stub (as discussed in the previous section). Here's an example service that exposesthe IRemoteService example interface to clients.
publicclass RemoteServiceextends Service { @Override
public voidonCreate() { super.onCreate();
}
@Override
public IBinder onBind(Intentintent) { // Return theinterface
returnmBinder;
}
private finalIRemoteService.Stub mBinder = new IRemoteService.Stub() { publicintgetPid(){
returnProcess.myPid();
}
publicvoidbasicTypes(intanInt, long aLong,boolean aBoolean, floataFloat,double aDouble,String aString){
// Does nothing
}
};
32
}
Now, when a client(suchas an activity) callsbindService()to connect tothisservice,the client's onServiceConnected() callback receives the mBinder instance returned by the service's onBind() method.
The client mustalso have access to theinterface class,so if the clientandservice are in separate applications,thenthe client's application must have a copy of the .aidl file in
its src/ directory (which generatesthe android.os.Binder interface—providingthe client accessto theAIDLmethods).
When the clientreceives the IBinder in the onServiceConnected() callback, it must call YourServiceInterface.Stub.asInterface(service) to cast the returned parameter to YourServiceInterfacetype.For example:
IRemoteService mIRemoteService;
private ServiceConnection mConnection =new ServiceConnection(){ // Called when theconnection withthe service isestablished
public voidonServiceConnected(ComponentNameclassName,IBinderservice) {
//Following theexample aboveforan AIDL interface,
//this gets an instance of the IRemoteInterface, which we canuse to call on the service mIRemoteService= IRemoteService.Stub.asInterface(service);
}
// Called when theconnection withthe service disconnectsunexpectedly public voidonServiceDisconnected(ComponentName className){
Log.e(TAG,"Service hasunexpectedly disconnected"); mIRemoteService= null;
}
}; |
|