IPC简介

IPC

IPC是Inter-Process Communication的缩写,含义为进程间通信或者跨进程通信,是指两个进程间进行数据交换的过程。

进程与线程

进程一般指一个执行单元,在PC或者移动设备上指一个程序或者一个应用;线程是CPU调度的最小单元,同时线程也是一种有限的系统资源。一个进程可以包含多个线程,两者是包含的关系。例如在Android应用中,默认都含有一个主线程,也叫做UI线程,用于操作一些界面绘制的任务,如果把一些耗时任务放在UI线程中,就会引起ANR(应用没有响应),所以必须将其放入到子线程中进行。

Binder

对于Android开发者来说,Binder应该是既熟悉又陌生,熟悉是因为在Android系统中它无处不在,陌生是因为它学习门槛高,从应用开发层贯穿至linux内核层,要深入了解你必须苦下功夫,阅读源码。所以binder很复杂,这边简要介绍binder使用以及上层原理。

Binder是Android的一个实现了Ibinder接口的类,它是一种跨进程的通信方式。在Linux内核层,它是一种虚拟的物理设备,它含有自己设备驱动在Linux内核层负责与硬件进行交互;在应用层与FrameWork层,Binder充当的是客户端与服务端进行消息交换的媒介,相当于一个中间层,对消息进行包装(封装成Binder对象),客户端就可以通过该对象来获取服务端的内容(客户端与服务端是可以随时互换的,并不是固定不变的)。

Android中的多进程模式

多进程设置

多进程的配置很简单,只要在Manifests配置文件中,给四大组件配上android:process属性即可,配置完运行即可在AndroidStudio或者Adb命令查看到多个进程以及相应的ID。

多进程运行机制

Android会为每个进程分配一个虚拟机,不同的虚拟机在内存分配上有不同的地址空间,这就导致不同的虚拟机访问同一个类会产生多个副本。例如:进程A与进程B,类C含有一个静态属性,在进程A中将其值改变,而在进程B中读取的还是之前未改变的值,这就产生了一个同步的差异。

多进程一般会产生一下几个问题:

1、静态成员与单例完全失效

2、线程同步机制完全失效

3、SharePreference可靠性下降

4、Apllication会创建多次

第1与第2个问题都是因为内存地址不同造成的;第3个问题是由于多进程并发读写Xml文件,可能造成数据丢失或者错乱;第四个问题是Android系统机制决定的,每个进程都会分配一个独立的虚拟机,这个过程其实就是启动一个应用的过程

Android中的IPC方式

由上可知,在多进程中不能像单进程一样操作,否则会造成很多潜在的问题,必须用进程间通信来处理跨域问题,以下是常见几种IPC方式

使用Bundle

通过Intent传递Bundle数据可用于实现组件(Activity、Service以及Receiver)之间消息通信,Bundle实现了Parceable接口,方便在不同进程间通信。Bundle支持的类型有基本类型、Parceable以及Serializable以及Android支持的特殊对象。例如:进程A的
aActivity直接用Intent(消息封装成Bundle对象)传递给进程B的bActivity,bActivity就能获取bundle中消息,实现进程间的通信了。

文件共享

文件共享顾名思义就是利用文件来存储数据,然后通过对文件的读写来实现数据共享。由上可知,多进程时可能会造成高并发操作文件,导致数据错乱或者不可靠,所以文件共享适合对数据同步要求不高的进程之间通信,并且要妥善处理并发读写问题。

AIDL

AIDL是应用接口定义语言,是用于实现进程间通信的,主要分为客户端与服务端。

服务端:

服务端首先要创建一个Service用来监听客户端的请求,然后创建一个AIDL文件,将暴露给客户端的接口在AIDL文件中声明,最后在Service中实现这个AIDL接口即可。

客户端:

客户端要做的事稍微简单点,首先需要绑定服务端的Service,绑定成功后,将服务端返回的Binder对象转换成AIDL接口所属的类型,接着就可以调用AIDL中的方法了。

对于AIDL文件的创建以及具体的代码事例这里就不进行介绍了,想了解的同学可以上网查找。

Messeneger

Messenger可以翻译为信使,通过它可以在不同进程间传递Message对象,将数据封装成Message,就可以轻松实现进程间传递了。Messenger是一种轻量级的IPC方案,它的底层实现是aidl,它是串行执行请求的,不能并发处理多个客户端的请求,所以它只适用于一些非高并发请求的场景。如下是它的实现流程图:

使用ContentProvider

ContentProvider是内容提供者,对于Android开发者来说这个应该不陌生,它是四大组件之一,它的搭档是ContentResolver(内容解析者)。底层是基于binder实现的,Android系统帮我们做好了封装,我们可以很简单的就能实现IPC。实现一个ContentProvider需要继承ContentProvider类,并且重写CRUD四个方法,并对其进行注册设置标识,其它应用或者进程通过ContentResolver与标识对其进行解析,这就实现了进程间的通信了。当然ContentProvider存储的媒介是多样的,可以是数据库、文件或者其它。

使用Socket

Socket也成为“套接字”,是网络通信中的概念,分为流式套接字以及用户数据报套接字两种,分别对应Tcp(可靠传输)与Udp(不可靠传输)协议。socket是基于客户端/服务器模式的,服务端启动一个服务监听客户端的连接,当有客户端接进来时,就可以进行消息传递了。客户端就是配置好ip以及端口号,发送连接请求给服务端即可。当然对于服务端的设计还是有点讲究的,需要考虑到多客户端接入处理效率以及资源的回收等问题,这里就不多做描述了,有兴趣的同学可以自己尝试。

总结

上面介绍的IPC方式多种多样,对于开发者来说选择合适的IPC方式是相当重要的,如下图:

当然Android的IPC岂是一篇博客能够讲完的,这里只是对其作了大致的介绍,对于binder、aidl以及Messenger等都没有做深入研究,有兴趣的同学可以阅读对应的源码进行研究探索。

参考书籍:《Android开发艺术探索》