IPC机制系列七 Android中的IPC方式(2)使用文件共享

使用文件共享

       共享文件也是一种不错的进程间通信方式,两个进程通过读/写同一个文件来交换数据,比如 A 进程把数据写入文件,B 进程通过读取这个文件来获取数据。我们知道,在 Windows 上,一个文件如果被加了排斥锁将会导致其它线程无法对其进行访问,包括读和写,而由于 Android 系统基于 Linux,使得其并发读/写文件可以没有限制地进行,甚至两个线程可以同时对同一个文件进行写操作都是允许的,尽管这可能出问题。通过文件交换数据很好使用,除了可以交换一些文本信息外,我们还可以序列化一个对象到文件系统中的同时从另一个进程中恢复这个对象。

       我们在MainActivity的onResume中序列化一个User对象到sd卡上的一个文件里,然后在SecondActivity的onResume中去反序列化,期望在SecondActivity中能够正确地恢复User对象的值。关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
new Thread(new Runnable() {
@Override
public void run() {
User user = new User(1, "hello world");
File dir = new File(Environment.getExternalStorageDirectory().getPath()+"/shareFile");
if (!dir.exists()) {
dir.mkdirs();
}
File cachedFile = new File(Environment.getExternalStorageDirectory().getPath()+"/shareFile/usercache");
ObjectOutputStream objectOutputStream = null;
try {
objectOutputStream = new ObjectOutputStream(
new FileOutputStream(cachedFile));
objectOutputStream.writeObject(user);
Log.d(TAG, "persist user:" + user);
} catch (IOException e) {
e.printStackTrace();
} finally {
MyUtils.close(objectOutputStream);
}
}
}).start();

new Thread(new Runnable() {
@Override
public void run() {
User user = null;
File cachedFile = new File(Environment.getExternalStorageDirectory().getPath()+"/shareFile/usercache");
if (cachedFile.exists()) {
ObjectInputStream objectInputStream = null;
try {
objectInputStream = new ObjectInputStream(
new FileInputStream(cachedFile));
user = (User) objectInputStream.readObject();
Log.d(TAG, "recover user:" + user);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
MyUtils.close(objectInputStream);
}
}
}
}).start();

       Github代码:MainActivitySecondActivity

       MainActivity和SecondActivity在两个不同的进程,查看log的话会发现,是成功恢复了User对象的内容的,这里之所以说内容,是因为反序列化得到的对象只是在内容上和序列化之前的对象是一样的,但它们本质上还是两个对象。

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

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

参考资料:
《Android 开发艺术探索》任玉刚 第2章 2.4.2 使用文件共享

Fork me on GitHub