Android面试——多进程通信,Binder机制和AIDL

为什么要使用 Binder 机制

Android 会为每个进程分配独立的虚拟内存空间,每个进程的虚拟地址空间是互相隔离的,如果进程间要进行互相通信,就要使用 Android 提供的 Binder 机制来进行通信。

进程间通信的方式

  1. 使用 Intent 来进行进程间通信,比如在调用百度地图的时候,使用 Intent ,用startActivity 来启动百度地图的指定页面

  2. 使用共享文件的方式,比如 A 、 B 进程要进行通信,那么A 进程可以把要通信的内容通过序列化的方式写入到本地文件,然后 B 进程再通过读取本地文件的方式获取A 要传递的内容

  3. 使用 Messager 或者AIDL(Android Interface Define Language,事实上 Messager 也是通过 AIDL 实现的,只是 Android 为我们进行了进行了简单的封装),AIDL 的底层也是通过 Binder 来完成进程间通信的。

  4. 使用 ContentProvider 进行进程间通信,作为四大组件,底层使用的也是 Binder 来完成进程间通信的

  5. 使用 Socket,Socket 可以实现计算机网络中的两个进程间通信,当然也可以用于进程间通信服务端监听指定的端口,客户端链接指定的端口,成功建立链接以后,拿到 socket 对象客户端就可以向服务端发送消息,或者接受服务端传来的消息。

什么是 Binder

Binder 是 Android 为我们提供的 IPC(Inner Process Commnuication进程间通信)的一种方式,Android 中的 Activity 、Service、Broadcast、ContentProvider 都是运行在不同的进程中,Binder 是他们之间进行通信的桥梁。

Binder 运行的原理

首先要定义要传输的对象,并进行序列化,反序列化,然后在相同包名下定义 AIDL 接口、要传输的对象,然后定义好了以后,再 makeProject ,生成对应的类,然后再创建一个 Service 写服务端的代码,然后再客户端使用 bindService 方法进行连接,再 ServiceConnection 的回调方法中拿到服务端的接口对象,之后就可以往服务端发送消息。

  1. AIDL 接口,是一个 interface,

  2. Sub 类,是 Binder 的实现类,服务端通过 Sub 类来提供服务

  3. Proxy 类,服务端的本地代理,客户端通过这个类调用服务端提供的方法。只有处于不同进程间通信的时候才会调用到,一个进程内不会使用这个类,直接使用 Sub 类就可以完成通信

  4. asInterface() 客户端调用,用来将 IBinder 对象转换为客户端需要的服务端的 AIDL 接口类型的对象。如果客户端和服务端处于同一个进程则返回 Sub ,不同进程返回 Sub.Proxy对象

  5. asBinder()根据当前调用情况返回代理Proxy的Binder对象

  6. onTransact() 运行在服务端的 Binder 线程池中,当客户端发起跨进程请求以后,请求会通过这个方法来处理

  7. transact() 运行在客户端,当客户端发起远程请求以后将当前线程挂起,之后调用服务端的 onTransact知 直到请求返回,当前线程才继续执行。

当多个模块需要 AID 来进行 IPC 的时候,此时需要创建多个 AIDL 文件,那么相应的 Service 就会有很多,必然会出现系统资源耗费严重,解决办法是创建 Binder 连接池,即将每个业务模块的 Binder 请求统一转发到一个远程 Service 中去执行,从而避免重复建立 Service。原理大概是:
每个业务模块创建自己的AIDL接口并实现此接口,然后向服务端提供自己的唯一标识和其对应的Binder对象。服务端只需要一个Service并提供一个queryBinder接口,它会根据业务模块的特征来返回相应的Binder对象,不同的业务模块拿到所需的Binder对象后就可以进行远程方法的调用了。

为什么使用 Binder

1. 效率高

传输效率主要影响因素是内存拷贝的次数,拷贝次数越少,传输速率越高,传统的 IPC 使用消息队列、Socket和管道,数据先从发送方的用户空间缓存区拷贝到内核空间开辟的缓存区,然后再从内核缓存区拷贝到接收方的用户空间缓存区,一共拷贝两次。

共享内存的方式不用拷贝,效率很高,但是实现的复杂度很高 。

而 Binder 只用拷贝一次 ,使用Binder 的话,进程 A、B通讯,只用拷贝数据到内核缓存区,内核缓存区和B 的缓存区是映射到同一块物理地址的,节省了一次拷贝的过程。

下面描述 Binder 传输过程(A进程向B进程传递数据)

  1. 首先 Binder 驱动在内核空间开辟一块 数据接收内存缓存区

  2. 接着在内核空间开辟一块 内核缓存区,建立内核缓存区和数据接收缓存区的映射关系,以及内核中的数据接收缓存区和B用户空间 缓存区的映射关系。

  3. 发送方进程 A 使用 copyfromuser(),将数据拷贝到内核缓存区,由于内核缓存区和数据接收缓存区有映射关系,数据接收缓存区和B进程的用户空间缓存区有映射关系,因此也就相当于把数据从 A 进程传递到了 B 进程。

2.稳定性好

上面说到共享内存的性能优于Binder,那为什么不采用共享内存呢,因为共享内存需要处理并发同步问题,容易出现死锁和资源竞争,稳定性较差。Socket虽然是基于C/S架构的,但是它主要是用于网络间的通信且传输效率较低。Binder基于C/S架构 ,Server端与Client端相对独立,稳定性较好。

3. 安全性

传统的 Linux IPC 的接收方无法获得对方进程可靠的 UID/PID,从而无法鉴别对方的身份,而Binder 机制为每个进程分配了 UID/PID,并且在 Binder 通信过程中会根据 UID/PID进行有效的身份验证。

(0)

相关推荐