Android开发艺术探索读书笔记: IPC机制

  Android中Binder机制的由四个部分组成:Binder驱动,Client,Server,ServerManager。其中Binder运行在内核空间,其余都运行在用户空间,Client、Server、ServerManager通过open/ioctl与Binder驱动交互。ServerManager用来管理Server并向Client提供了Server接口查询。类比与网络中的通信方式,Client为客户端,Server为服务器端,ServerManager为DNS服务器,Binder驱动做为通信链路,Client向ServerManager查询服务端的地址,并通过Binder驱动与Server建立通信。

Binder驱动做为进程间的媒介运行在内核空间,通过系统调用向Binder驱动写控制命令+数据,然后等待Binder驱动唤醒然后读取结果数据。通过代理,在Java层调用对象方法就像调用本地方法一样,其实是将要调用的方法与参数封装成特定的数据结构(序列化),写到内核驱动,服务端再将数据解析(反序列化),调用方法得到结果写回Binder驱动,客户端再拿到结果。(数据只拷贝一次)

Binder架构

(图片来自老罗的Android之旅)

Binder机制使用进程虚拟地址空间和内核虚拟地址空间映射同一个物理页面,进程与内核之间就减少了一次内存的拷贝,提高通信的效率。

Android中的多进程模式

线程是CPU最小的调度单元,而进程是执行单元,表现为一个应用程序。进程与线程之间是包含关系。

应用间的进程相当于两个应用采用ShardUid的模式

IPC基础概念

 private void witeObject(OutputStream os){
    //...
 }

 private void readObject(InputStrem is) {
    //...
 }

另外比较重要的就是serialVersionUID(long)这个常量,根据这个变量名可以知道是序列化的版本号,不写也是可以的,使用这个类的时候系统会给我们生成,利用类的成员变量hash给我们算出来,所以一方面这就加重了系统的负担,不一致的话反序列化就直接抛异常。所以指定这个版本号的情况下,我们增加个成员变量或者减少个成员变量,系统会尽可能地给我们反序列化,但类发生了非常规变化(比如类名变了),还是会抛异常。
当序列化的版本号不一致时,反序列化抛的异常是 java.io.InvalideClassException,所以一般我们都指定值为1L,或者使用IDE给我们生成。

Client->Binder: 远程请求 Binder-->Client:挂起 Binder-->Binder:写入data Binder->Service:调用transact传递data Service-->ThreadPool:onTransact把服务端方法放\n到线程池执行 ThreadPool-->ThreadPool:写入reply ThreadPool-->Binder:返回结果 Binder-->Client:唤醒Client并返回数据

关于Binder的线程池官方文档有这样的描述:

The system maintains a pool of transaction threads in each process that it runs in. These threads are used to dispatch all IPCs coming in from other processes. For example, when an IPC is made from process A to process B, the calling thread in A blocks in transact() as it sends the transaction to process B. The next available pool thread in B receives the incoming transaction, calls Binder.onTransact() on the target object, and replies with the result Parcel.

我们在AndroidStudio Debug的时候可以直观地看到:
AndroidStudio里面看见的Binder线程池.png

Android中的IPC方式

Binder连接池

主要思想就是服务端提供获取各类Binder的接口并返回,客户端取得合适的Binder对象,避免创建多个Service。实现代码参考