Android多进程通讯方式
AIDL 功能较强大 支持进程间一对多的实时并发通信,并且可以实现RPC(远程过程调用)
Messenger
四大组件的进程间通信
文件共享
Socket 主要通过网络传输数据
首先我们需要了解什么是进程,进程和线程是两种完全不同的概念,从操作系统的角度来看,线程是CPU调度的最小单元,而进程一般指的是一个执行单元。在我们的Android系统中,一般一个进程指的是一个应用程序,但是不能说一个应用程序中只能有一个进程,一个应用程序中可以有多个进程,市面上主流的App,都是多进程的。一个进程中可以包含多个线程,因此进程和线程是包含与被包含的关系。在最简单的情况下,一个进程可以只有一个线程,即主线程,在Android里面主线程也叫UI线程,在UI线程里才能做更新UI界面操作。在主线程中是不允许进行耗时操作的,比如网络请求等等,因为那样会造成应用程序卡顿,可能导致应用程序ANR。为了避免ANR,我们一般都是把耗时操作放在子线程中进行。
为什么需要多进程
1. Android系统给单个应用可以使用的最大内存做了限制,有些app需要突破这个限制,想给应用程序获取更多的内存空间,那么可以通过多进程来获取更多的内存空间。
2. 有些特殊的模块需要不影响主模块的稳定性和占用主模块的内存。以及不受主业务进程生命周期影响,独立存在和运行。比如我们开发中常用的推送功能就需要使用多进程来实现。
3. app保活,有些app为了在后台运行时不被系统杀掉,往往会用多进程的方式来实现进程间互相监督,互相拉活来实现app保活。
多进程缺点
1. Application多次重建
2. 静态成员变量和单例模式完全失效
3. 线程同步机制完全失效
4. SharedPreferences可靠性降低
对于造成上面第三个问题的原因其实很简单,因为在多进程情况下,他们都不在同一块内存区域,那么不管是锁对象还是锁全局类都无法保证线程同步。因为不同进程锁的对象都不一样。静态变量和单例模式失效的原因就是因为系统为每个进程都单独分配了一个独立的虚拟机,这样就导致在不同的虚拟机中访问同一个类会产生多份副本。所以静态成员变量和单例模式自然也就失效了。对于SharedPreferences可靠性降低降低是因为SharedPreferences不支持两个进程同时对其进行操作。因为SharedPreferences的底层就是通过读写XML文件来实现的。并发的去读写肯定是会出问题的。
IPC
IPC不是Android系统上独有的,任何一个操作系统都需要相应的IPC机制来实现跨进程通信,window上面我们可以通过剪切板,管道等进行进程间通信,Linux中可以通过共享内存,信号量等来实现跨进程通信。 我们都知道Android系统是基于Linux开发的,但是它有自己的跨进程通信方式。
通常android系统中应用程序之间不能共享内存。因此,在不同应用程序之间交互数据(跨进程通讯)就稍微麻烦一些。
进程间通信(ipc)
IPC方法总是产生客户/服务端模式的调用,也即是客户端组件(Activity/Service)持有服务端Service的组件,只能是客户端主动调用服务端的方法,服务端无法反过来调用客户端的方法,因为IPC的另一端Service无法获取客户端的对象。
binder
Binder 是一种进程间通信机制。安卓中跨进程通讯就是通过binder。当绑定服务的时候会返回一个binder对象,然后通过他进行多进程间的通信。Binder只需要一次数据拷贝,性能上仅次于共享内存。
在 Android 系统中,这个运行在内核空间,负责各个用户进程通过 Binder 实现通信的内核模块就叫 Binder 驱动(Binder Dirver)。
Binder IPC 机制中涉及到的内存映射通过 mmap() 来实现,mmap() 是操作系统中一种内存映射的方法。内存映射简单的讲就是将用户空间的一块内存区域映射到内核空间。映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间;反之内核空间对这段区域的修改也能直接反应到用户空间。
内存映射能减少数据拷贝次数,实现用户空间和内核空间的高效互动。两个空间各自的修改能直接反映在映射的内存区域,从而被对方空间及时感知。也正因为如此,内存映射能够提供对进程间通信的支持。
Binder IPC 正是基于内存映射(mmap)来实现的
Binder 通信中的代理模式
我们已经解释清楚 Client、Server 借助 Binder 驱动完成跨进程通信的实现机制了,但是还有个问题会让我们困惑。A 进程想要 B 进程中某个对象(object)是如何实现的呢?毕竟它们分属不同的进程,A 进程 没法直接使用 B 进程中的 object。
前面我们介绍过跨进程通信的过程都有 Binder 驱动的参与,因此在数据流经 Binder 驱动的时候驱动会对数据做一层转换。当 A 进程想要获取 B 进程中的 object 时,驱动并不会真的把 object 返回给 A,而是返回了一个跟 object 看起来一模一样的代理对象 objectProxy,这个 objectProxy 具有和 object 一摸一样的方法,但是这些方法并没有 B 进程中 object 对象那些方法的能力,这些方法只需要把把请求参数交给驱动即可。对于 A 进程来说和直接调用 object 中的方法是一样的。
当 Binder 驱动接收到 A 进程的消息后,发现这是个 objectProxy 就去查询自己维护的表单,一查发现这是 B 进程 object 的代理对象。于是就会去通知 B 进程调用 object 的方法,并要求 B 进程把返回结果发给自己。当驱动拿到 B 进程的返回结果后就会转发给 A 进程,一次通信就完成了
其实进程间通信就是为了实现数据共享。一个程序不同组件在不同进程也叫多进程,和俩个应用没有本质区别。使用process属性可以实现多进程,但是会带来很多麻烦,主要原因是共享数据会失败,弊端有:静态和单利失效,同步失效,sharedprefer也变的不可靠等问题。
使用多进程显而易见的好处就是分担主进程的内存压力。我们的应用越做越大,内存越来越多,将一些独立的组件放到不同的进程,它就不占用主进程的内存空间了。当然还有其他好处,有些应用后台是有多个进程的,启动一个不可见的轻量级私有进程,在后台收发消息,或者做一些耗时的事情,或者开机启动这个进程,然后做监听等。还有就是防止主进程被杀守护进程,守护进程和主进程之间相互监视,有一方被杀就重新启动它。因为它们要常驻后台,特别是即时通讯或者社交应用