1. Bundle

我们知道,四大组件中的三大组件(Activity、Service、BroadcastReceiver)都是支持 Intent 中传递 Bundle 数据的,由于 Bundle 实现了 Parcelable 接口,所以它可以方便的在进程间传输。基于这一点,当我们在一个进程中启动了另一个进程的 Activity、Service 或者 BroadcastReceiver,我肯就可以在 Bundle 中附加我们需要传输给远程进程的信息通过 Intent 发送过去。当然,我们的数据必须能够序列化,

除了传递数据这种典型使用场景,它还有一种特殊的使用场景,比如 A 进程正在进行一个计算,计算完成后,它需要把计算结果传递给 B 进程,可是遗憾的是这个计算结果不支持放入 Bundle 中,因此无法通过 Intent 来传输,这个时候如果我们使用其他 IPC 方式就会略显复杂,可以考虑如下方式:我们通过 Intent 启动进程 B 的一个 Service 组件,让 Service 在后台进行计算,计算完成后,在启动 B 进程中真正要启动的组件,由于 Service 业运行在 B 进程中,所以目标组件就可以直接获取计算结果,这样一来就轻松解决了跨进程的问题。

2. 使用文件共享

共享文件是一种不错的进程间通信方式,两个进程通过读/写同一个文件交换数据,比如 A 进程把数据写入文件,B 进程通过读取这个文件来获取数据,但是有一点需要注意,由于 Android 系统是基于 Linux 的,使得其并发读/写文件可以没有限制的执行,甚至多个进程对同一个文件进行写操作都是允许的,这样就会导致数据异常。

还是采用本章刚开始的那个例子,这次我们在 MainActivity 的 onResume 中序列化一个 UserBean 对象打动 SD 卡上的一个文件里,然后再 SecondActivity 的 onResume 中反序列化,我们期望在 SecondActivity 能正常回复 UserBean 对象的值,如下所示:

UserBean userBean = new UserBean("小明", "男", 18);
        File file = new File(Environment.getExternalStorageDirectory(), "text.txt");
        ObjectOutputStream out;
        try {
            out = new ObjectOutputStream(new FileOutputStream(file));
            out.writeObject(userBean);
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

SecondActivity 修改代码如下:

File file = new File(Environment.getExternalStorageDirectory(), "text.txt");
        //反序列化
        ObjectInputStream in;
        try {
            in = new ObjectInputStream(new FileInputStream(file));
            UserBean user = (UserBean) in.readObject();
            Log.e(getClass().getName(), user.getName());
            Log.e(getClass().getName(), user.getSex());
            Log.e(getClass().getName(), user.getAge() + "");
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

有一点需要注意的是,ObjectInputStream 和 ObjectOutputStream 是序列化和反序列化流,这里针对的是一个对象的操作,如果该对象可以转化成 json 数据,建议使用 FileWriter 和 FileReader 或者 FileInputSteam 和 FileOutputSteam 来处理。这里就不验证了。

通过文件共享这种方式来共享数据对文件格式是没有限制的,比如可以使文本文件,也可以是 XML 文件,只要读/写双方约定数据格式即可。通过文件共享的方式也是有局限的,比如并发读/写的问题,像上面的例子,如果并发读,那么我们独处的内容就有可能不是最新的,如果是并发写的话那问题就更严重了。因此我们要尽量避免并发写这种情况的发生或者考虑使用线程同步来限制多个线程的写操作。通过上面的分析,我们可以知道,文件共享适合在对数据同步要求不高的进程之间进行通信,并且要妥善处理并发读/写的问题。

当然,ShaaredPerferences 是个特例,众所周知,ShaaredPerferences 是 Android 中提供的轻量级存储方案,它通过键值对的方式来存储数据,在底层上采用 XML 文件来存储键值对,每个应用的 ShaaredPerferences 文件都可以在当前包所在的 data 目录下找到,一般来说,它的目录位于 /data/data/packageName/share_prefs 目录下。从本质上来说,ShaaredPerferences 也属于文件的一种,但是系统对它的读/写有一定的缓存策略,即在内存中会有一份 ShaaredPerferences 文件的存储,因此在多进程模式下,系统对它的读/写变得不可靠,当面对高并发读/写访问,ShaaredPerferences 有很大几率丢失数据,因此,不建议在进程间通信使用 ShaaredPerferences。