@JeromeLiee
2020-01-12T23:25:01.000000Z
字数 2591
阅读 756
未分类
IPC(Inter-Process Communication)
,进程间通信,就是指多个进程间互相通信、交换信息的方法。
由于Android是基于Linux系统的,所以会拥有Linux的IPC方式,例如管道、共享内存、套接字等,以及Android自己独有的Binder。
管道的原理是在内核开辟一块共享内存作为数据缓冲区,当进行跨进程交互时,先从发送方进程的缓冲区拷贝数据到内核的数据缓冲中,然后接收方再从内核缓冲区拷贝数据到自己进程的缓冲区。
由于需要拷贝两次,效率不是很高,所以Android并没有采取这种方式。
套接字多用于网络设备之间的数据传输方式,并且也涉及到多次I/O操作,所以效率不是非常高。
共享内存的原理是开辟一块内存供多个进程使用,与管道不同的是,每个进程可以将共享内存的地址映射到自己进程的地址空间中,当进行跨进程交互时,发送方根据地址映射修改共享内存中的数据,接收方可以通过地址映射关系,直接从共享内存中读取数据。因此共享内存的方式是不需要进行数据拷贝操作,效率很高,是所有IPC方式中效率最高的。
但由于该内存是被所有进程共享,存在并发问题以及安全问题,所以Android也并没有直接采取这种IPC方式,而是采用了效率和安全相对兼得的Binder方式。
在Linux系统中,为了保证系统安全,会将系统内核空间和用户空间分开,防止用户程序崩溃而影响到整个系统。所以用户进程之间的通信,必须通过内核空间来完成整个过程。Binder采用了C/S架构,使得用户进程和系统进程隔离,数据统一由系统服务进程处理,保证了稳定性和安全性。另外在数据传输过程中只需要一次数据拷贝,性能仅次于内存共享方式。
在基于C/S架构下,定义了4个角色:Client、Server、ServerManager和Binder驱动。Client、Server和ServerManager是位于用户空间彼此独立的进程,它们之间无法通信,Binder驱动位于系统内核空间,负责进程间通信的建立,以及数据的传递交互等间。
ServerManager(后面简称SM)是一个独立的系统服务进程,用于管理所有的Server进程。SM进程比较特殊,当一个进程将自己注册为SM进程时,Binder驱动会为该进程创建一个Binder实体,而这个Binder实体的引用在其它所有的Client进程中都固定为0。也就是说,一个Server进程想要在ServerManger中注册自己的Binder时,通过0这个引用号便可以获取ServerManger进程的Binder,然后通过Binder将自己的进程注册到ServerManager中,生成一个对应的Binder实体。
当Server在SM中注册生成了对应的Binder之后,Client也可以通过自身保留的0引用号,根据Server的名称获取对应的Binder引用。这样就建立起了Client端和Server端之间的通信,如下图所示:
当建立起了Binder通信之后,Binder驱动会分配一块内存作为缓存池,用来接收和存放数据。这块内存空间会映射到Server端。当数据从发送方拷贝到内核中,由于这层映射关系的存在,接收方可直接访问内核中的数据,无需再将数据从内核中拷贝到接收方中,相较传统的IPC方式,其效率提升一倍。
一般会有个抽象功能类,对应一个本地代理类和一个远程端的代理类。当获取到远程Server端的Binder后,会通过本地的代理类,调用远程Binder的transact()方法将数据传递给远程端,此时本地端线程挂起。而远程端代理通过onTransact()方法收到数据,并且根据方法code执行对应的方法。执行完毕会将结果封装至reply再通过Binder驱动返回给本地端。本地端收到远程端返回的数据后线程恢复,对数据进行本地处理,此时整个Binder跨进程通信结束。
Android中的序列化是实现Parcelable接口,IDE会帮我们重写一系列的方法,其中序列化的过程是通过调用Parcel的一系列write方法,将JavaBean的成员变量按照顺序写入到一个Parcel对象中,这个Parcel对象可以在Binder中传输。反序列化就是将通过调用一系列的read方法,从Parcel对象中按照顺序读取字段,然后创建对象。
Serializable使用简单,但其序列化和反序列化都需要进行I/O操作,开销比较大,而Parcelable实现序列化主要用于内存中,开销小,方便跨进程Binder中传输,缺点是实现起来比较麻烦,另外想要进行持久化存储或网络传输也比较麻烦。
所以如果是想要进行持久化存储数据或用于网络传输,那么使用Serializable,如果只是想用于Binder跨进程传输,那么优先使用Parcelable。