Binder 概述
- Binder和传统的 IPC 相比,融合了远程过程调用(RPC)的概念。而这种远程调用不是传统的面相过程调用,而是面向对象调用。
- 从 Unix 发展而来的 IPC 机制,只能提供比较原始的 IPC 通信手段,通信的双方必须处理线程同步,内存管理等复杂的问题,不但工作量大而且很容易出错。除了 Socket 和 匿名管道(Pipe)以外,传统的 IPC 如:命名管道(FIFO)、信号量(Semaphore)、消息队列等已经从Android中去掉了。和其他 IPC 方式比较,Socket 用于网络通信比较合适,但是用于进程通信效率就不太高了。
- Android 在架构上一直希望模糊进程的概念,取而代之以组件的概念。应用不需要关心组件的存放位置,组件运行在哪个进程中,组件的生命周期等问题。岁数随地的只要拥有 Binder 对象,就能使用组件功能。Binder 就像一张网将整个系统的组件,跨越进程和线程,组织在了一起。
- Binder 很强大也很复杂,但是无论是调用 Binder 还是开发一个Binder服务都很简单。不需要考虑线程同步和内存分配问题,因为所有的这些麻烦都在 Binder 框架中解决了。正因为如此所有的 Android 系统服务都是用 Binder 构建的,android 系统服务有几十种之多。
- Binder 是整个系统运行的中枢,一般 IPC 通信的方式是将数据从 用户进程复制到内核,再从内核复制到服务进程,进行了两次复制操作。Binder 为了提高效率,采用了共享内存的方式,这样数据只需要复制一次就能从一个进程到另外一个进程。
Binder 对象定义
Binder 实体对象
Binder 实体对象就是 Binder 服务的提供者。一个提供 Binder 服务的类必须继承 BBinder 类,因此有时为了强调类型,也用 BBinder 对象来代替 Binder 实体对象。
Binder 引用对象
Binder 引用对象是 Binder 实体对象在客户进程的代表,每个引用对象的类型都是 BpBinder 类,可以称呼为 BpBinder 对象。
Binder 代理对象
代理对象也称为接口对象,它主要为客户端的上层应用提供接口服务,从 IInterface 类派生。它实现了 Binder 服务的函数接口,当然只是一个转调的空壳,通过代理对象,应用能像使用本地对象一样使用远端的实体对象提供的服务。
IBinder 对象
Binder 和 BpBinder 类都是从 IBinder 中继承而来,在很多场合不用区分实体对象和引用对象,都可以称为 IBinder 对象。
- Binder 代理对象主要和应用程序打交道,将 Binder 代理对象和引用对象分开的好处是代理对象可以有很多实例,但是他们包含的是同一个引用对象,这样方便了用户层的使用。应用完全可以抛开接口对象直接使用 Binder 的引用对象,但这样开发程序兼容性不好。也正是客户端将引用对象和代理对象分离,Android 才能用一套架构同时为 Java 和 Native 层提供 Binder 服务。隔离后底层不需要关心上层的实现细节,只需要和Binder实体对象和引用对象进行交互。
Binder 架构
Binder 通信的参与者由四部分组成
- Binder 驱动,Binder 的核心,实现各种 Binder 的底层操作。
- ServiceManager :提供 Binder 的名称到引用对象的转换服务。
- 服务端:Binder 服务的提供者。
- 客户端:Binder 服务的使用者。
Binder 服务架构图如下
- Binder 驱动位于 Binder 架构的核心,通过文件系统的标准接口,如:open()、ioctl() 、mmap() 等 像用户层提供服务,应用层和Binder之间的驱动是通过 ioctl() 完成的,read() 和 write() 两个接口反倒没有使用。Binder 的数据交换过程比较复杂,而且涉及到两个进程之间的数据传输,如果用两个不同接口来分别实现,会导致实现比现在更复杂。使用 ioctl() 的好处是一次系统调用就能完成用户系统和驱动之间的双向数据交换,不但简化了控制逻辑,也提高了传输效率。Binder 驱动主要功能是提供Binder通信的通道,维护 Binder 对象的引用计数,转换 Binder 中的实体对象和引用对象以及管理数据的缓存区。
- ServiceManager 是一个守护进程,他的作用是提供Binder的查询功能,返回被查询服务的引用。对于一个Binder服务,通常会有一个唯一的字符串标识,只要它向 ServiceManager 注入了这个标识,应用就可以通过这个标识获取服务的引用对象。ServiceManager是一个单独的进程,实际上它也是通过Binder 框架为其他应用提供服务。
- 作为一个 Binder 服务提供者,ServiceManager 也需要通过某种方式让使用者得到它的引用对象。Android使用了一种很巧妙也很简单的方法来解决这个问题。Binder对象的核心数据是一个实体对象的引用号,它是在系统内部分配的一个值,Binder 框架硬性规定了 0 代表 ServiceManager ,这样用户可以直接通过参数 0 构建出 ServiceManager 对象然后开始使用 ServiceManager 查询服务。
- 既然驱动是 Binder 的核心,为什么不直接把查找引用对象的功能放在驱动中完成呢,还要在 ServiceManager 中绕一圈。其实这和android的安全管理有关系,Android 中并不允许任意进程都能注册 Binder 服务,虽然任意一个进程都能创建 Binder 服务,但是只有 root 进程和 System 进程可以不受限制的向 ServiceManager 注册服务。ServiceManager 中有一张表里面规定了能够注册服务的进程ID,以及进程能够注册服务的服务名称,ServiceManager 通过这张表控制普通进程的注册请求
- Binder服务可以分为两种,实名服务和匿名服务。他们从开发到使用没有任何区别,唯一的区别是实名服务可以通过 ServiceManager 查询到。Android 中的实名Binder 服务都是系统提供的如 AMS,PMS ,WMS等,普通应用开发 BInder 服务只能是匿名服务。
- 如果匿名服务不能通过 ServiceManager 查询到,那么使用什么方式可以获取到 Binder引用对象呢?还是通过 Binder 服务。匿名服务通常使用的场景是服务进程回调客户进程的函数。客户端和服务端通过 Binder 连接上以后,客户端把本进程创建的匿名服务实体对象通过参数传递到服务端,驱动会把实体对象转换为引用对象,这样服务进车跟就可以通过客户端进程创建的Binder服务引用对象,然后就可以回调客户端的Binder函数了。
组件 Service 和匿名 Binder 服务
在 Java 层,Android 还提供了通过组件 Service 的方式来包装和使用匿名服务。组件 Service 中也包含了 Binder 服务,但是我们谈论组件 Service 时,更多的是关心它的生命周期,启动和停止相关。而 Binder 跟生命周期没什么太大关系,所以两者不能混为一谈。匿名服务因为没有 ServiceManager 提供名称解析服务,因此一般只能服务进程回调客户端进程。但是 Android 还通过 Framework 提供了一种启动 Binder 匿名服务的方法。过程如下:首先某个应用通过 BindService() 方法发送 Intent ,Framework 根据 Intent 找到对应的组件 Service 并启动它,组件Service 中的 Binder 服务也一起创建出来。随后 Framework 会把创建的 Binder 对象通过 ConnectivityManager 的回调方法 onServiceConnect() 传回到应用,这样应用就得到了匿名 Binder 服务引用对象,也就是能使用 Service 的Binder服务了。在这里 Android 的 Framework 用 Intent 代替了 Binder 服务的名称来查找对象的服务,同时也承担了 ServiceManager 的工作,解析 Intent 并传回 Binder 服务。
因为 AMS 本身就是一个 Binder 服务,所以最终还是通过 Binder 框架在服务端和客户端传递 IBinder 对象。
Binder 的层次
从代码上划分,Binder类可以分为四个层次,如下图:
- 最上层是位于 Framework 层中的各种 Binder 服务类和他们的接口类,这一层实现类非常多,比如 AMS,PMS ,WMS 等,他们为应用提供各种各样的服务。
- 最底层是 Binder 驱动
- 中间层分为两层,上层是用于服务类和接口类开发的基础,如 IBinder ,Binder ,BPBinder 下层是和驱动交互的 IPCThreadState 和 ProcessState 类。
- 在这四层中,第一层和第二层联系很紧密,第二层中的各种 Binder 类用于支撑服务类和代理类的开发。但是第三层的 IPCThread 和 第四层的驱动耦合很厉害,单独理解 IPCThread 和 驱动都是很难的,因为驱动和应用之间过于耦合。
如何使用 Binder
- C++ 层和 Java 层使用 Binder 服务的方式基本一样,包括函数和接口类型都一样。使用 Binder 服务首先要得到它的引用对象
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->checkService(String16("media.camera.proxy"));
defaultServiceManager() 用来获取 ServiceManager 服务引用对象,ServiceManager 的 getService() 和 checkService() 作用是查找注册的 Binder 服务,区别是 getService() 是阻塞式的 checkService() 是非阻塞式的。如果找到对应的 Binder 服务就返回 Binder 对象,否则返回 NULL。返回值类型是 IBinder ,实际上是引用对象,但是应用需要使用的是 Binder 代理对象,因此使用前需要把引用对象转换为代理对象。
if (binder != nullptr) {
proxyBinder = interface_cast<ICameraServiceProxy>(binder);
}
interface_cast 实际上是下面的代码 asInterface() 方法会检查参数重对象的服务类型是否相符,如果不符则返回 NULL,得到了代理对象以后记下来就可以用Binder服务了。
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
return INTERFACE::asInterface(obj);
}
- 正常情况下 ServiceManager 会在所有应用前启动,并且不会停止,因此 defaultServiceManager(); 方法可以不用检查 NULL,但是 checkService() 或者 getService() 需要判断是否为 NULL。
Binder 的混合调用
- Binder 也是可以混合调用的,在 C++代码中可以调用 Java 语言编写的 Binder 服务,从Java语言中也可以调用 C++ 编写的 Binder 服务。调用的方法就是直接使用服务的引用对象。Binder 服务端用号码来表示它所提供的函数,客户端要使用某个函数号来表示要调用的函数。函数号不能很方便的获取,所以这种方法很少使用。