一. 前言
一直想把Binder机制认识清楚, 但是它涉及Android系统的Framework, Native, kernel层, 就需要你要有 C++ C基础阅读底层源码的能力, 目前笔者的水平,对Binder 在Native 和kernel层的实现原理和机制也是懵逼状态, 真的是博大精深, 故现阶段先把看懂和理解清楚的整理出来, 重点归纳Binder在app层和Framework层实现机制. 后续在慢慢积累往底层去理解它吧.
二. IPC通信
Binder作为Android系统提供的一种IPC(Inter-process communication)机制,无论从系统开发还是应用开发,都是Android系统中最重要的组成,也是最难理解的一块知识点.
Android系统中,多进程间的通信都是依赖于底层Binder IPC机制,Binder机制是一种RPC方案。例如:当进程A中的Activity与进程B中的Service通信时,就使用了binder机制.
2.1 我们先引入第一个问题: Android系统中有哪些IPC通信方式?
名称 | 特点 | 使用场景 |
---|---|---|
Bundle | 只能传输实现了序列化或者一些Android支持的特殊对象 | 适合用于四大组件之间的进程交互 |
文件 | 不能做到进程间的即时通信,并且不适合用于高并发的场景 | 适合用于SharedPreference以及IO操作 |
ContentProvider | 可以访问较多的数据,支持一对多的高并发访问,因为ContentProvider已经自动做好了关于并发的机制 | 适合用于一对多的数据共享并且需要对数据进行频繁的CRUD操作 |
Socket | 通过网络传输字节流,支持一对多的实时通信,但是实现起来比较复杂 | 适合用于网络数据共享 |
Messenger | 底层原理是AIDL,只是对其做了封装,但是不能很好的处理高并发的场景,并且传输的数据只能支持Bundle类型 | 多进程、单线程且线程安全 |
AIDL | 功能强大,使用Binder机制,支持一对多的高并发实时通信,但是需要处理好线程同步 | 一对多并且有远程进程通信的场景 |
2.2 安卓中既然这么多IPC通信方式,为什么推荐使用Binder呢?
Android系统的基础是Linux内核,而Linux中实现IPC的机制有管道、消息队列、共享内存、Socket、信号量、信号这些,为什么Android还要另起炉灶呢?主要是性能、安全、易用性等方面的原因。
性能上来说数据拷贝次数越少越好,什么是拷贝下文再介绍。传统IPC机制(Socket、管道、消息队列等)都是拷贝两次,共享内存虽然无需拷贝,但会有安全、死锁、易用性差等问题,Binder只需拷贝一次,因此性能仅次于共享内存优于Socket。
安全性上来说Binder会为每个APP分配唯一的UID,Binder根据UID可以找到对应APP,传统IPC依赖上层协议是不安全的,无法获得对方的UID从而不能鉴别身份。传统IPC访问接入点是开放的,相当于谁都可以访问;Binder既有实名服务又有匿名服务,实名就跟传统IPC一样,谁都可以访问,AMS、WMS都是实名服务,匿名类似于打滴滴,用户直接联系不到司机,需要通过滴滴(系统)平台拿到司机号码,通过系统拿到服务的代理对象,再通过代理对象找到服务。Binder实名和匿名区别在于有没有在ServiceManager注册,注册了即为实名,没注册称为匿名,我们自己使用AIDL等方式一般为匿名。
3种常用IPC机制对比
Binder | 共享内存 | Socket | |
---|---|---|---|
性能 | 一次拷贝 | 无需拷贝 | 两次拷贝 |
特点 | 基于C/S架构, 易用性高 | 控制复杂, 易用性差 | 传输效率低, 开销大 |
安全性 | 为每个APP分配UID, 同时支持实名和匿名 | 依赖上层协议, 访问接入点是开放的 不安全 | 依赖上层协议, 访问接入点是开放的 不安全 |
如果想了解更多? 请参阅 为什么Android要采用Binder作为IPC机制?
三. Binder IPC原理
Binder通信采用c/s架构,从组件视角来说,包含Client、Server、ServiceManager以及binder驱动,其中ServiceManager用于管理系统中的各种服务。
Client进程
使用服务的进程。
Server进程
提供服务的进程。
ServiceManager进程
Android OS的整个服务的管理程序,在binder通信中ServiceManager的作用是将字符形式的Binder名字转化成Client中对该Binder的引用,使得Client能够通过Binder名字获得对Server中Binder实体的引用。
任何service在被使用之前,均要向SM(Service Manager)注册。当客户端需要访问某个service时,应该首先向SM查询是否存在该服务。如果SM存在这个service,那么会将该service的handle返回给client,handle是每个service的唯一标识符。
Binder
一个进程的Binder线程数默认最大是16,超过的请求会被阻塞等待空闲的Binder线程。所以,在进程间通信时处理并发问题时,如使用ContentProvider时,它的CRUD(创建、检索、更新和删除)方法只能同时有16个线程同时工作
Binder驱动
驱动负责进程之间Binder通信的建立,Binder在进程之间的传递,Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。
Binder通信分层架构图
Binder架构也是采用分层架构设计, 每一层都有其不同的功能:
已 调用startService方法流程为例子, 引出跨进程通信的架构图
- Java应用层: 对于上层应用通过调用AMP.startService, 完全可以不用关心底层,经过层层调用,最终必然会调用到AMS.startService.
- Java IPC层: Binder通信是采用C/S架构, Android系统的基础架构便已设计好Binder在Java framework层的Binder客户类BinderProxy和服务类Binder;
- Native IPC层: 对于Native层,如果需要直接使用Binder(比如media相关), 则可以直接使用BpBinder和BBinder(当然这里还有JavaBBinder)即可, 对于上一层Java IPC的通信也是基于这个层面.
- Kernel物理层: 这里是Binder Driver, 前面3层都跑在用户空间,对于用户空间的内存资源是不共享的,每个Android的进程只能运行在自己进程所拥有的虚拟地址空间, 而内核空间却是可共享的. 真正通信的核心环节还是在Binder Driver.
四. 安卓系统IPC图
如上图,Android系统分成三层。最上层是application应用层,第二层是Framework层,第三层是native层。 由下图可知几点:
1、Android中的应用层和系统服务层不在同一个进程,系统服务在单独的进程中。
2、Android中不同应用属于不同的进程中。
Android应用和系统services运行在不同进程中是为了安全,稳定,以及内存管理的原因,但是应用和系统服务需要通信和分享数据。
优点
安全性:每个进程都单独运行的,可以保证应用层对系统层的隔离。
稳定性:如果某个进程崩溃了不会导致其他进程崩溃。
内存分配:如果某个进程以及不需要了可以从内存中移除,并且回收相应的内存。
五. Thanks
上面是Binder机制架构知识点, 我们对进程间通信架构先有个总体印象,代码细节和实现再慢慢探究, 文章图是引用 gityuan博客:Binder系列7—framework层分析 - Gityuan博客 | 袁辉辉的技术博客