Android开发艺术探索知识回顾——第2章|Android开发艺术探索知识回顾——第2章 IPC机制(2、不同的 IPC 方式)

2.4 Android 中的 IPC 方式 在上节中,我们介绍了 IPC 的几个基础知识:序列化和 Binder,本节开始详细分析各种跨进程通信方式。具体方式有很多,比如可以通过在 Intent 中附加 extras 来传递信息,或者通过共享文件的方式来共享数据,还可以采用 Binder 方式来跨进程通信,
另外, Contentprovider 天生就是支持跨进程访问的,因此我们也可以釆用它来进行 IPC。此外,通过网络通信也是可以实现数据传递的,所以 Socket 也可以实现 IPC。上述所说的各种方法都能实现 IPC,它们在使用方法和侧重点上都有很大的区别,下面会一一进行展开。
2.4.1 使用 Bundle 我们知道,四大组件中的三大组件(Activity、Service、Receiver)都是支持在 Intent 中传递 Bundle 数据的,由于 Bundle 实现了 Parcelable 接口,所以它可以方便地在不同的进程间传输。基于这一点,当我们在一个进程中启动了另一个进程的 Activity。Service 和 Receiver,我们就可以在 Bundle 中附加我们需要传输给远程进程的信息,并通过Intent发送出去。
当然,我们传输的数据必须能够被序列化,比如基本类型、实现了Parcellable接口的对象、实现了 Serializable 接口的对象以及 些 Android 支持的特殊对象,具体内容可以看 Bundle 这个类,就可以看到所有它支持的类型。Bundle 不支持的类型我们无法通过它在进程间传递数据,这个很简单,就不再详细介绍了。这是一种最简单的进程间通信方式,
除了直接传递数据这种典型的使用场景,它还有一种特殊的使用场景。比如 A 进程正在进行一个计算,计算完成后它要启动 B 进程的一个组件并把计算结果传递给 B 进程,可是遗憾的是这个计算结果不支持放入 Bundle 中,因此无法通过 Intent 来传输,这个时候如果我们用其他 IPC 方式就会略显复杂。可以考虑如下方式:我们通过 Intent 启动进程 B 的一个 Service 组件(比如IntentService)。让 Service 在后台进行计算,计算完毕后再启动 B 进程中真正要启动的目标组件,由于 Service 也运行在 B 进程中,所以目标组件就可以直接获取计算结果,这样一来就轻松解决了跨进程的问题。这种方式的核心思想在于将原本需要在 A 进程的计算任务转移到 B 进程的后台 Service 中去执行,这样就成功地避免了进程间通信问题,而且只用了很小的代价。
2.4.2 使用文件共享 共享文件也是一种不错的进程间通信方式,两个进程通过读/写同一个文件来交换数据,比如 A 进程把数据写入文件,B 进程通过读取这个文件来获取数据。我们知道,在 Windows上,一个文件如果被加了排斥锁将会导致其他线程无法对其进行访问,包括读和写,而由于Android 系统基于 Linux,使得其并发读/写文件可以没有限制地进行,甚至两个线程同时对同一个文件进行写操作都是允许的,尽管这可能出问题。通过文件交换数据很好使用,除了可以交换一些文本信息外,我们还可以序列化一个对象到文件系统中的同时,从另一个进程中恢复这个对象,下面就展示这种使用方法。
还是本章刚开始的那个例子,这次我们在 MainActivity 的 onResume 中序列化一个 User 对象到 sd 卡上的一个文件里,然后在SecondActivity 的 onResume 中去反序列化,我们期望在 SecondActivity 中能够正确地恢复User对象的值。关键代码如下:

下面看一下log,很显然,在 SecondActivity 中成功地从文件从恢复了之前存储的 User 对象的内容,这里之所以说内容,是因为反序列化得到的对象只是在内容上和序列化之前的对象是一样的,但它们本质上还是两个对象。
通过文件共享这种方式来共享数据对文件格式是没有具体要求的,比如可以是文本文 件,也可以是XML文件,只要读/写双方约定数据格式即可。通过文件共享的方式也是有局限性的,比如并发读/写的问题,像上面的那个例子,如果并发读/写,那么我们读出的内容就有可能不是最新的,如果是并发写的话那就更严重了。
因此我们要尽量避免并发写这种情况的发生,或者考虑使用线程同步来限制多个线程的写操作。通过上面的分析,我们可以知道,文件共享方式,适合在对数据同步要求不高的,进程之间进行通信,并且要妥善处理并发读/写的问题。
当然,SharedPreferences是个特例,众所周知,SharedPreferences 是 Android 中提供的轻量级存储方案,它通过键值对的方式来存储数据,在底层实现上它采用 XML 文件来存储键值对,每个应用的 SharedPreferences 文件都可以在当前包所在的 data 目录下査看到。—般来说,它的目录位于 /data/data/package name/shared_prefs 目录下,其中 package name 表示的是当前应用的包名。
从本质上来说,SharedPreferences 也属于文件的一种,但是由于系统对它的读/写有一定的缓存策略,即在内存中会有一份SharedPreferences 文件的缓存,因此在多进程模式下,系统对它的读/写就变得不可靠,当面对高并发的读/写访问,Sharedpreferences有很大几率会丢失数据,因此,不建议在进程间通信中使用 SharedPreferences

2.4.3 使用 Messenger Messenger 可以翻译为信使,顾名思义,通过它可以在不同进程中传递 Message 对象, 在Message中放入我们需要传递的数据,就可以轻松地实现数据的进程间传递了。Messenger 是一种轻量级的 IPC 方案,它的底层实现是AIDL,为什么这么说呢,我们大致看一下 Messenger 这个类的构造方法就明白了。下面是 Messenger 的两个构造方法,从构造方法的实现上我们可以明显看出 AIDL 的痕迹,不管是 IMessenger 还是 Stub.aslnterface,这种使用方法都表明它的底层是 AIDL。

Messenger 的使用方法很简单,它对 AIDL 做了封装,使得我们可以更简便地进行进程间通信。同时,由于它一次处理一个请求,因此在服务端我们不用考虑线程同步的问题,这是因为服务端中不存在并发执行的情形。实现一个 Messenger 有如下几个步骤,分为服务端和客户端。

整理中。。。




【Android开发艺术探索知识回顾——第2章|Android开发艺术探索知识回顾——第2章 IPC机制(2、不同的 IPC 方式)】

    推荐阅读