本节引言:
本节,我们继续来研究Service(服务)组件,本节将会学习下Android中的AIDL跨进程通信的一些 概念,并不深入到源码层次,暂时知道是什么,会用即可!开始本节内容~ 本节对应官方文档:Binder
1.Binder机制初涉
1)IBinder和Binder是什么鬼?
我们来看看官方文档怎么说:
中文翻译:
IBinder是远程对象的基本接口,是饿了高性能而设计的轻量级远程调用机制的核心部分。但他 不仅用于远程调用,也用于进程内调用。该接口定义了与远程对象间交互的协议。但不要直接实现 这个接口,而是继承(extends)Binder。
IBinder主要的API是transact(),与之对应的API是Binder.onTransact()。通过前者,你能 想远程IBinder对象发送发出调用,后者使你的远程对象能够响应接收到的调用。IBinder的API都是 Syncronous(同步)执行的,比如transact()直到对方的Binder.onTransact()方法调用玩 后才返回。 调用发生在进程内时无疑是这样的,而在进程间时,在IPC的帮助下,也是同样的效果。
通过transact()发送的数据是Parcel,Parcel是一种一般的缓冲区,除了有数据外还带有 一些描述它内容的元数据。元数据用于管理IBinder对象的引用,这样就能在缓冲区从一个进程移动 到另一个进程时保存这些引用。这样就保证了当一个IBinder被写入到Parcel并发送到另一个进程中, 如果另一个进程把同一个IBinder的引用回发到原来的进程,那么这个原来的进程就能接收到发出的 那个IBinder的引用。这种机制使IBinder和Binder像唯一标志符那样在进程间管理。
系统为每个进程维护一个存放交互线程的线程池。这些交互线程用于派送所有从另外进程发来的IPC 调用。例如:当一个IPC从进程A发到进程B,A中那个发出调用的线程(这个应该不在线程池中)就阻塞 在transact()中了。进程B中的交互线程池中的一个线程接收了这个调用,它调用 Binder.onTransact(),完成后用一个Parcel来做为结果返回。然后进程A中的那个等待的线程在 收到返回的Parcel后得以继续执行。实际上,另一个进程看起来就像是当前进程的一个线程, 但不是当前进程创建的。
Binder机制还支持进程间的递归调用。例如,进程A执行自己的IBinder的transact()调用进程B 的Binder,而进程B在其Binder.onTransact()中又用transact()向进程A发起调用,那么进程A 在等待它发出的调用返回的同时,还会用Binder.onTransact()响应进程B的transact()。 总之Binder造成的结果就是让我们感觉到跨进程的调用与进程内的调用没什么区别。
当操作远程对象时,你经常需要查看它们是否有效,有三种方法可以使用:
- 1 transact()方法将在IBinder所在的进程不存在时抛出RemoteException异常。
- 2 如果目标进程不存在,那么调用pingBinder()时返回false。
- 3 可以用linkToDeath()方法向IBinder注册一个IBinder.DeathRecipient, 在IBinder代表的进程退出时被调用。
PS:中文翻译摘自 : Android开发:什么是IBinder
好吧,估计你看完上这一串东西可能云里雾里的,这里简单的小结下:
IBinder是Android给我们提供的一个进程间通信的一个接口,而我们一般是不直接实现这个接口的, 而是通过继承Binder类来实现进程间通信!是Android中实现IPC(进程间通信)的一种方式!
2)Binder机制浅析
Android中的Binder机制由一系列系统组件构成: Client、Server、Service Manager和Binder驱动程序
大概调用流程如下,另外Service Manager比较复杂,这里并不详细研究!
流程解析:
-> Client调用某个代理接口中的方法时,代理接口的方法会将Client传递的参数打包成Parcel对象;
-> 然后代理接口把该Parcel对象发送给内核中的Binder driver;;
-> 然后Server会读取Binder Driver中的请求数据,假如是发送给自己的,解包Parcel对象, 处理并将结果返回;
PS:代理接口中的定义的方法和Server中定义的方法是一一对应的, 另外,整个调用过程是一个同步的,即Server在处理时,Client会被Block(锁)住! 而这里说的代理接口的定义就是等下要说的AIDL(Android接口描述语言)!
3)为何Android使用Binder机制来实现进程间的通信?
- 可靠性:在移动设备上,通常采用基于Client-Server的通信方式来实现互联网与设备间的内部通信。目前linux支持IPC包括传统的管道,System V IPC,即消息队列/共享内存/信号量,以及socket中只有socket支持Client-Server的通信方式。Android系统为开发者提供了丰富进程间通信的功能接口,媒体播放,传感器,无线传输。这些功能都由不同的server来管理。开发都只关心将自己应用程序的client与server的通信建立起来便可以使用这个服务。毫无疑问,如若在底层架设一套协议来实现Client-Server通信,增加了系统的复杂性。在资源有限的手机 上来实现这种复杂的环境,可靠性难以保证。
- 传输性能:socket主要用于跨网络的进程间通信和本机上进程间的通信,但传输效率低,开销大。消息队列和管道采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的一块缓存区中,然后从内核缓存区拷贝到接收方缓存区,其过程至少有两次拷贝。虽然共享内存无需拷贝,但控制复杂。比较各种IPC方式的数据拷贝次数。共享内存:0次。Binder:1次。Socket/管道/消息队列:2次。
- 安全性:Android是一个开放式的平台,所以确保应用程序安全是很重要的。Android对每一个安装应用都分配了UID/PID,其中进程的UID是可用来鉴别进程身份。传统的只能由用户在数据包里填写UID/PID,这样不可靠,容易被恶意程序利用。而我们要求由内核来添加可靠的UID。 所以,出于可靠性、传输性、安全性。android建立了一套新的进程间通信方式。 ——摘自:Android中的Binder机制的简要理解
当然,作为一个初级的开发者我们并不关心上述这些,Binder机制给我们带来的最直接的好处就是: 我们无需关心底层如何实现,只需按照AIDL的规则,自定义一个接口文件, 然后调用调用接口中的方法,就可以完成两个进程间的通信了!
2.AIDL使用详解
1)AIDL是什么?
嘿嘿,前面我们讲到IPC这个名词,他的全名叫做:跨进程通信(interprocess communication), 因为在Android系统中,个个应用程序都运行在自己的进程中,进程之间一般是无法直接进行数据交换的, 而为了实现跨进程,Android给我们提供了上面说的Binder机制,而这个机制使用的接口语言就是: AIDL(Android Interface Definition Language),他的语法很简单,而这种接口语言并非真正的编程 语言,只是定义两个进程间的通信接口而已!而生成符合通信协议的Java代码则是由Android SDK的 platform-tools目录下的aidl.exe工具生成,生成对应的接口文件在:gen目录下,一般是:Xxx.java的接口! 而在该接口中包含一个Stub的内部类,该类中实现了在该类中实现了IBinder接口与自定义的通信接口, 这个类将会作为远程Service的回调类——实现了IBinder接口,所以可作为Service的onBind( )方法的返回值!
2)AIDL实现两个进程间的简单通信
在开始编写AIDL接口文件前,我们需要了解下编写AIDL的一些注意事项:
AIDL注意事项:
- 接口名词需要与aidl文件名相同
- 接口和方法前面不要加访问权限修饰符:public ,private,protected等,也不能用static final!
- AIDL默认支持的类型包括Java基本类型,String,List,Map,CharSequence,除此之外的其他类型都 需要import声明,对于使用自定义类型作为参数或者返回值,自定义类型需要实现Parcelable接口, 详情请看后面的传递复杂数据类型
- 自定义类型和AIDL生成的其它接口类型在aidl描述文件中,应该显式import,即便在该类和定义 的包在同一个包中。
另外,如果编写aidl你用的编译器是:Eclipse的话要注意: 不要直接new file然后建立哦!这样的话是打不开文件,从而不能编写代码哦!
①直接新建一个txt文件,编写好后保存为.aidl格式,然后复制到对应路径下
②因为aidl