一、AIDL简介
由于每个应用程序都运行在自己的进程空间,并且可以从应用程序UI运行另一个服务进程,而且经常会在不同的进程间传递对象。在Android平台,一个进程通常不能访问另一个进程的内存空间,所以要想访问的话,需要将对象分解成操作系统可以理解的基本单元,并且有序的通过进程边界。通过代码来实现这个数据传输过程是冗长,但是Android为开发者提供了AIDL工具来处理这项工作。
AIDL (Android Interface Definition Language)是一种IDL语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。AIDL IPC机制是面向接口的,但是更加轻量级。它是使用代理类在客户端和实现端传递数据。
AIDL(AndRoid接口描述语言)是一种接口描述语言,编译器可以通过aidl文件生成一段代码,通过预先定义的接口达到两个进程内部通信进程的目的。如果需要在一个Activity中,访问另一个Service中的某个对象,需要先将对象转化成AIDL可识别的参数(可能是多个参数),然后使用AIDL来传递这些参数,在消息的接收端,使用这些参数组装成自己需要的对象。
AIDL定义一个接口,客户端(调用端)通过bindService来与远程服务端建立一个连接,在该连接建立时会将返回一个IBinder对象,该对象是服务端Binder的BinderProxy,在建立连接时,客户端通过asInterface函数将该BinderProxy对象包装成本地的Proxy,并将远程服务端的BinderProxy对象赋值给Proxy类的mRemote字段,就是通过mRemote执行远程方法调用。
二、AIDL适用的场景
在官方文档中有提醒我们何时有必要使用AIDL:只有你允许客户端从不同的应用程序为了进程间的通信而去访问你的service,以及想在你的service处理多线程。具体不同场景下的AIDL设计规则如下:
1如果不需要进行不同应用程序间的并发通信(IPC),通过实现一个Binder对象来创建接口;
2或者你想进行IPC,但不需要处理多线程的,则使用Messenger对象来创建接口。
三、AIDL的开发步骤
1.创建AIDL文件
Android需要AIDL定义远程接口。AIDL接口定义语言的语法很简单,它只是定义两个进程之间的通信接口。AIDL定义接口的源代码必须以.aidl结尾。AIDL接口中用到数据类型,除了基本类型、String、List、Map、CharSequence以外,其余的数据类型都需要导包,即使它们就在同一个包中也是需要导包的。开发人员定义的AIDL接口只是定义了进程之间的通信接口,Service端、客户端都需要使用AndroidSDK安装目录下的platform-tools子目录下的aidl.exe工具为该接口提供实现,如果开发者使用ADT工具进行开发,那么ADT工具会自动为该AIDL接口生成实现。
图1
如图1所示,我们在音乐播放器工程中的src的cn.XXX.service的包下创建IMediaPlaybackService.aidl和ITaskCallback.aidl。IMediaPlaybackService.aidl的代码如图2所示。
图2
定义好上面的AIDL接口后,ADT工具会自动在gen目录下生成IMediaPlaybackService.java和ITaskCallback.java接口,如图3所示。在接口中会包含Stub内部类,如图4所示,该内部类实现了IMediaPlaybackService接口,可以作为Service的onBind()方法的返回值。
图3
图4
2.将接口暴露给客户端
定义好AIDL接口之后,接下来就可以定义一个Service实现类了,如图5所示为定义的MediaPlaybackService,该Service的onBind()方法所返回的IBinder对象应该是ADT所生成的ICat.Stub的子类的实例,如图6所示为该Service的onBind()方法,图7是返回的IMediaPlaybackService.Stub的实例,其中IMediaPlaybackServiceStub继承IMediaPlaybackService.Stub,实现了IBinder接口。至于其他部分,和开发本地的Service一致。
图5
图6
图7
该Service类开发完成之后,还需在AndroidManifest.xml文件中配置该Service。
3.客户端访问AIDLService
AIDL接口定义了两个进程之间的通信接口,因此不仅服务器端需要AIDL接口,客户端同样需要定义的AIDL接口,因此开发客户端的第一步就是将Service端的AIDL接口文件复制到客户端应用中,复制到客户端后ADT工具会为AIDL接口生成相应的实现。
客户端绑定远程Service与绑定本地Service的区别不大,同样只需要两步。创建ServiceConnection对象。以ServiceConnection对象作为参数,调用Context的bindService()方法绑定远程Service即可。
与绑定本地Service不同的是,绑定远程Service的ServiceConnection并不能直接获取Service的onBind()方法所返回的对象,它只能返回onBind()方法所返回的对象的代理。
在音乐播放器代码中自定义ServiceUtil类,通过bindToService()方法和unbindToService()方法,进行绑定和启动Service,以及解除绑定。如图8和图9所示。在Activity中启动Service,只需在onCreate()中添加:
mToken=ServiceUtil.bindToService(this);
解除绑定可在onDestroy()中添加:
ServiceUtil.unbindFromService(mToken);
图8
图9
四、音乐播放器的Service
1.MediaPlaybackService.java实现了音乐播放器的所有Service实现方法。
2.IMediaPlaybackService.aidl使用AIDLService机制实现客户端与Service之间通信。
3.ITaskCallback.aidl是在线搜索歌曲的回调接口,只有语音助手使用。
4.MediaPlayvbackServiceStub.java继承了IMediaPlaybackService.Stub,方便MediaPlaybackService进行实例化对象。
5.ServiceUtil.java定义ServiceUtil类能实现客户端和MediaPlaybackService的通信。
五、音乐播放器的Activity同Service通信交互
图10 音乐播放器的Activity同Service通信交互
1.Activity通过在onCreate()中调用ServiceUtil类的bindToService()方法启动或绑定MediaPlaybackService。
2.在MediaPlaybackService的onBind()返回IBinder对象的IMediaPlaybackService.Stub的实例。
3.Activity调用ServiceUtil类的方法,ServiceUtil通过继承自IMediaPlaybackService.Stub的sService实例实现同MediaPlaybackService的通信完成相应的命令。
4.当MediaPlaybackService完成相应的任务,发送PLAYSTATE_CHANGED的广播,Activity通过registerReceiver接收广播。
5.通过IMediaPlaybackService .Stub.asInterface(service)来得到sService对象。
六、总结
(1)接口名和aidl文件名相同。
(2)接口和方法前不用加访问权限修饰符public,private,protected等,也不能用final,static。
(3)AIDL默认支持的类型包括java基本类型(int,long,boolean等)和(String,List,Map,CharSequence),使用这些类型时不需要import声明。对于List和Map中包含的元素类型必须是AIDL支持的类型.如果使用自定义类型作为参数或返回值,自定义类型必须实现Parcelable接口。
(4)自定义类型和AIDL生成的其它接口类型在aidl描述文件中,应该显式import,即使在同一个包中。
(5)在ServiceUtil中通过IMediaPlaybackService .Stub.asInterface(service)来得到sService对象,并通过sService实现同MediaPlaybackService的通信。