[关闭]
@JeromeLiee 2020-01-12T15:25:01.000000Z 字数 2591 阅读 686

Android IPC机制

未分类


参考

3分钟带你看懂android中的Binder机制

linux基础——linux进程间通信(IPC)机制总结

Android Binder设计与实现 - 设计篇

1. 什么是IPC机制?

IPC(Inter-Process Communication),进程间通信,就是指多个进程间互相通信、交换信息的方法。

2. IPC方式

由于Android是基于Linux系统的,所以会拥有Linux的IPC方式,例如管道、共享内存、套接字等,以及Android自己独有的Binder。

2.1 管道

管道的原理是在内核开辟一块共享内存作为数据缓冲区,当进行跨进程交互时,先从发送方进程的缓冲区拷贝数据到内核的数据缓冲中,然后接收方再从内核缓冲区拷贝数据到自己进程的缓冲区。

由于需要拷贝两次,效率不是很高,所以Android并没有采取这种方式。

2.2 套接字

套接字多用于网络设备之间的数据传输方式,并且也涉及到多次I/O操作,所以效率不是非常高。

2.2 共享内存

共享内存的原理是开辟一块内存供多个进程使用,与管道不同的是,每个进程可以将共享内存的地址映射到自己进程的地址空间中,当进行跨进程交互时,发送方根据地址映射修改共享内存中的数据,接收方可以通过地址映射关系,直接从共享内存中读取数据。因此共享内存的方式是不需要进行数据拷贝操作,效率很高,是所有IPC方式中效率最高的。

但由于该内存是被所有进程共享,存在并发问题以及安全问题,所以Android也并没有直接采取这种IPC方式,而是采用了效率和安全相对兼得的Binder方式。

3. Binder的实现机制

在Linux系统中,为了保证系统安全,会将系统内核空间和用户空间分开,防止用户程序崩溃而影响到整个系统。所以用户进程之间的通信,必须通过内核空间来完成整个过程。Binder采用了C/S架构,使得用户进程和系统进程隔离,数据统一由系统服务进程处理,保证了稳定性和安全性。另外在数据传输过程中只需要一次数据拷贝,性能仅次于内存共享方式。

3.1 Binder通信模型

在基于C/S架构下,定义了4个角色:Client、Server、ServerManager和Binder驱动。Client、Server和ServerManager是位于用户空间彼此独立的进程,它们之间无法通信,Binder驱动位于系统内核空间,负责进程间通信的建立,以及数据的传递交互等间。

3.2 ServerManager和Binder

ServerManager(后面简称SM)是一个独立的系统服务进程,用于管理所有的Server进程。SM进程比较特殊,当一个进程将自己注册为SM进程时,Binder驱动会为该进程创建一个Binder实体,而这个Binder实体的引用在其它所有的Client进程中都固定为0。也就是说,一个Server进程想要在ServerManger中注册自己的Binder时,通过0这个引用号便可以获取ServerManger进程的Binder,然后通过Binder将自己的进程注册到ServerManager中,生成一个对应的Binder实体。

3.3 Client和Binder

当Server在SM中注册生成了对应的Binder之后,Client也可以通过自身保留的0引用号,根据Server的名称获取对应的Binder引用。这样就建立起了Client端和Server端之间的通信,如下图所示:

undefined

3.4 拷贝一次数据

当建立起了Binder通信之后,Binder驱动会分配一块内存作为缓存池,用来接收和存放数据。这块内存空间会映射到Server端。当数据从发送方拷贝到内核中,由于这层映射关系的存在,接收方可直接访问内核中的数据,无需再将数据从内核中拷贝到接收方中,相较传统的IPC方式,其效率提升一倍。

3.5 Client和Server通信的实现

一般会有个抽象功能类,对应一个本地代理类和一个远程端的代理类。当获取到远程Server端的Binder后,会通过本地的代理类,调用远程Binder的transact()方法将数据传递给远程端,此时本地端线程挂起。而远程端代理通过onTransact()方法收到数据,并且根据方法code执行对应的方法。执行完毕会将结果封装至reply再通过Binder驱动返回给本地端。本地端收到远程端返回的数据后线程恢复,对数据进行本地处理,此时整个Binder跨进程通信结束。

4. 序列化

Android中的序列化是实现Parcelable接口,IDE会帮我们重写一系列的方法,其中序列化的过程是通过调用Parcel的一系列write方法,将JavaBean的成员变量按照顺序写入到一个Parcel对象中,这个Parcel对象可以在Binder中传输。反序列化就是将通过调用一系列的read方法,从Parcel对象中按照顺序读取字段,然后创建对象。

Serializable使用简单,但其序列化和反序列化都需要进行I/O操作,开销比较大,而Parcelable实现序列化主要用于内存中,开销小,方便跨进程Binder中传输,缺点是实现起来比较麻烦,另外想要进行持久化存储或网络传输也比较麻烦。

所以如果是想要进行持久化存储数据或用于网络传输,那么使用Serializable,如果只是想用于Binder跨进程传输,那么优先使用Parcelable。

4. 多进程引发的问题

  1. 静态成员和单例模式完全失效
    不同虚拟机不同内存,所以失效
  2. 线程同步机制完全失效
    本质和上述问题原因相同
  3. SharedPreferences的可靠性下降
    SharedPreferences不支持两个进程同时去执行写操作,会导致一定概率的数据丢失。SharedPreferences底层是通过读/写xml文件来实现的,并发写显然是可能出问题的,甚至并发读/写都有可能出现问题。
  4. Application会多次创建
    重新创建虚拟机,所以会重新创建Application。
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注