一、引言
Android系统已经有其他的IPC方式,管道,Socket等,为什么还要使用Binder呢?
1.1 性能方面
Binder数据拷贝只需要一次,管道,消息队列,Socket都需要两次
共享内存实现方式较复杂
1.2 安全方面
传统通信方面对身份并没有做出严格的验证,比如Socket通过IP通信很容易伪造
Binder支持双方身份的验证,安全性高
二、Binder概述
2.1 IPC机制
客户端有Client进程空间和一个共享的内核空间,Server进程也有自己的用户空间和共享内核空间,每个进程独享自己的数据和空间,这是进程隔离,如图
2.2 Binder通信中的四个角色
2.2.1 用户空间实现(3个)
Client进程:使用服务,开发者自定义实现
Server进程:注册服务,开发者自定义实现
ServerManager进程:将字符形式Binder转化为Client中Binder的引用,是一个守护进程,用来管理Server,并向Client提供查询Server接口的能力
2.2.2 内核空间实现(1个)
Binder驱动:底层进程通信建立,Binder进程间传递,已在Android平台实现,开发者只需要调用即可
2.3 Binder运行机制
如图
调用Server进程向ServerManager注册服务,Client进程向ServerManager获取服务,获取服务之后Client进程与Server进程建立通信通路,开始交互。
2.4 一个例子
跨进程调用系统服务例子:
浮动窗口添加
//获取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);
在Android开机过程中Android初始化会各种服务,客户端想要服务直接通过ServiceManager获取即可
代码第二行即获取一个WindowManager的引用
代码最后一行远程调用addView,调用的是运行在systemServer进程中的WindowManager的addView函数
2.5 一个完整的服务调用过程
client调用server接口方法,server将client传递的参数打包成一个Parcel对象然后发送给内核中的binder driver —— 一个请求完成
server读取binder driver的请求数据,确认是发给自己的处理解析Parcel对象并将结果返回 —— 一个请求处理完成
整个调用过程是同步的,server在处理的时候client会被阻塞,所以client的调用不应该在主线程 —— 总结
三、一些问题
3.1 为什么Binder只进行了一次数据拷贝
Linux内核并没有从一个用户空间到另一个用户空间直接拷贝的函数,拷贝需要先将数据拷到内核空间,然后再从内核空间拷贝到其他进程空间
假设有两个进程A,B
为了实现用户到用户的空间的直接拷贝,在分配内存的时候,内存地址不仅仅被映射到A用户空间,还被映射到了内核空间,所以在A进程进行拷贝时相当于直接把数据拷贝到了B进程的用户空间,所以B进程只需要进行一次拷贝即可获取A进程用户空间。
参考文章:
https://www.jianshu.com/p/4920c7781afe?from=jiantop.com