Android跨进程通信、Binder与Aidl
作者:互联网
Android为什么使用binder通信
Android系统中,涉及到多进程间的通信底层都是依赖于Binder IPC机制。例如当进程A中的Activity要向进程B中的Service通信,这便需要依赖于Binder IPC。不仅于此,整个Android系统架构中,大量采用了Binder机制作为IPC(进程间通信)方案。
当然也存在部分其他的IPC方式,如管道、SystemV、Socket等。那么Android为什么不使用这些原有的技术,而是要使开发一种新的叫Binder的进程间通信机制呢?
为什么要使用Binder?
性能方面
在移动设备上(性能受限制的设备,比如要省电),广泛地使用跨进程通信对通信机制的性能有严格的要求,Binder相对出传统的Socket方式,更加高效。Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,共享内存方式一次内存拷贝都不需要,但实现方式又比较复杂。【文章最下面有binder一次拷贝的解释】
安全方面
传统的进程通信方式对于通信双方的身份并没有做出严格的验证,比如Socket通信ip地址是客户端手动填入,很容易进行伪造,而Binder机制从协议本身就支持对通信双方做身份校检,因而大大提升了安全性。
Binder使用原理
首先我们看看我们的程序跨进程调用系统服务的简单示例,实现浮动窗口部分代码:
//获取WindowManager服务引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);
//布局参数layoutParams相关设置略...
View view=LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);
//添加view
wm.addView(view, layoutParams);
注册服务(addService):在Android开机启动过程中,Android会初始化系统的各种Service,并将这些Service向ServiceManager注册(即让ServiceManager管理)。这一步是系统自动完成的。
获取服务(getService):客户端想要得到具体的Service直接向ServiceManager要即可。客户端首先向ServiceManager查询得到具体的Service引用,通常是Service引用的代理对象,对数据进行一些处理操作。即第1行代码中,得到的wm是WindowManager对象的引用。
使用服务:通过这个引用向具体的服务端发送请求,服务端执行完成后就返回。即第3行调用WindowManager的addView函数,将触发远程调用,调用的是运行在systemServer进程中的WindowManager的addView函数。
使用服务的具体执行过程
Client就是房客,Server就是房东,ServiceManager就是房屋中介,每个Server如果要提供服务就必须要去ServiceManager那里去注册,ServiceManager在一张查找表中记录一个Server的名字,对应着Server的引用。Client想要获得Server,必须通过名字到ServiceManager取找Server的引用,获得这个Server的binder引用,通过这个binder引用去和Server通信。
1.client通过获得一个server的代理接口,对server进行调用。
2.代理接口中定义的方法与server中定义的方法时一一对应的。
3.client调用某个代理接口中的方法时,代理接口的方法会将client传递的参数打包成Parcel对象。
4.代理接口将Parcel发送给内核中的binder driver。
5.server会读取binder driver中的请求数据,如果是发送给自己的,解包Parcel对象,处理并将结果返回。
6.整个的调用过程是一个同步过程,在server处理的时候,client会block住。因此client调用过程不应在主线程。
7.client和ServiceManager也不是同一个进程,为什么不需要通过binder驱动来通信呢?因为对一般的Service组件来说,Client进程首先要通过Binder驱动程序来获取它的一个句柄值,然后通过这个句柄创建一个Binder代理对象,最后将这个Binder代理对象封装成一个实现特定接口的代理对象。ServiceManager也相当于一个service,是一个守护进程,但由于ServiceManager的句柄值为0,因此获取它的代理对象不需要跟Binder驱动程序进程交互。
Aidl的意义
1.aidl主要就帮助了我们完成了包装数据和解包的过程,并调用了transact过程,而用来传递的数据包我们就称为parcel,最终获取跨进程binder并调用其中相关内容
2.用aidl定义需要被调用方法接口,实现这些方法,调用这些方法
3.底层是binder。代码上就是接口,使用interface声明,但其中只能使用简单数据类型,复杂数据类型如某类则需要创新其对应aidl文件并实现相关函数才能使用
Aild的使用理解
以IPackageManager、PackageManager、PackageManagerService解释AIDL
- IPackageManager负责通信。IPackageManager接口类中定义了很多业务方法,但是由于安全等方面的考虑,Android对外(即SDK)提供的仅仅是一个子集,该子集被封装在抽象类PackageManager中。客户端一般通过Context的getPackageManager函数返回一个类型为PackageManager的对象,该对象的实际类型是PackageManager的子类ApplicationPackageManager。ApplicationPackageManager并没有直接参与Binder通信,而是通过mPM成员变量指向了一个IPackageManager.Stub.Proxy类型的对象
- AIDL中的Binder服务端是PackageManagerService,因为PackageManagerService继承自IPackageManager.Stub。由于IPackageManager.Stub类从Binder派生,所以PackageManagerService将作为服务端参与Binder通信。
- AIDL中的Binder客户端是ApplicationPackageManager中成员变量mPM,因为mPM内部指向的是IPackageManager.Stub.Proxy
比喻解释:
假设你是一个公司的商务负责人,正在和客户商谈事务,在涉及公司的具体业务的同事,你要请示你的老板,你需要给你老板打电话,交流商务谈判的具体细节,这里面,你就是应用进程里面的ApplicationPackageManager,IPackageManager就是你们的通信工具——电话,你老板就是SystemServer进程里面的PackageManagerService,你的电话就是IPackageManager.Stub.Proxy,老板的电话是IPackageManager.Stub。IPackageManager其实就是一个具体业务场景下的数据交换的工具而已。
Binder与Aidl
【为什么binder只使用了一次内存拷贝】
回忆下我们在Android中如果需要做文件读写操作,是不是必须要申请权限呢?为什么要申请呢? 因为用户空间的权限低于内核空间,但是用户空间又需要访问内核空间的资源 一般情况下,操作系统会提供一些系统调用接口,通过系统调用接口,用户程序可以在内核的控制下实现对内核资源的有限访问,这样既能保证满足应用程序的资源请求,又能保障系统安全和稳定。
传统IPC机制通信流程【共享内存机制除外】
- 首先,发送方进程通过系统调用将要发送的数据从用户空间copy到内核空间缓存区中
- 接着,接收方开辟一段内存空间,然后通过系统调用将内核缓存区中的数据copy到接收方的内存缓存区,而Linux是使用的虚拟内存寻址方式,用户空间和内核缓存区的虚拟内存地址是映射到物理内存中的,so实际上是对物理内存的读写(内存映射)
- 这种传统方式存在两个问题:
需要做2次数据拷贝操作
接收方进程在接收数据之前,需要事先分配空间来存取数据,但不知道事先要分配多大的空间,这样就可能存在一种在空间上的浪费
- Binder借助了内存映射的方法,在
内核空间和接收方用户空间的数据缓存区之间做了一层内存映射
,就相当于直接拷贝到了接收方用户空间的数据缓存区,从而减少了一次数据拷贝
标签:调用,Aidl,binder,进程,Binder,IPackageManager,ServiceManager,Android 来源: https://www.cnblogs.com/1118zjg/p/16009924.html