Android夸进程通信机制系列:
Android夸进程通信机制一:多进程简介
Android夸进程通信机制二:Parcel 与 Parcelable
Android夸进程通信机制三:Messenger与Message
Android夸进程通信机制四:使用 Bundle进行进程间通信
Android夸进程通信机制五:使用文件共享进行进程间通信
Android夸进程通信机制六:使用ContentProvider进行进程间通信
Android夸进程通信机制七:使用 Socket进行进程间通信
Android夸进程通信机制八:使用 AIDL进行进程间通信
Android夸进程通信机制九:AIDL深入了解
...
一、前言
前面几节已经讲了,基本数据及可序列化数据在进程间是如何传送的,也就是Messenger把装有Bundle的Message发送到别的进程,这样就完成进程间的通信了。
那么,还有其他的方式实现进程间的通信吗,当然有啦,之前讲过,进程间的通信方式有:Messenger,Bundle,文件共享,Content Provider……,我们这一节讲一下文件共享
二、文件共享
共享文件也是一种不错的进程间通信方式,两个进程通过读/写同一个文件来交换数据,比如 A 进程把数据写入文件,B 进程通过读取这个文件来获取数据。
我们知道,在 Windows 上,一个文件如果被加了排斥锁将会导致其他线程无法对其进行其他访问,包括读和写,而由于 Android 系统基于 Linux,使得其并发读/写文件可以没有限制的进行,甚至两个线程可以同时对一个文件进行写操作都是允许的,尽管这可能会出问题。通过文件交换数据很好使用,除了可以交换一些文本信息外,我们还可以序列化一个对象到文件系统中的同时从另一个进程中恢复这个对象。
三、使用文件共享
我们还是老样子,通过实例来加深理解,我们这个栗子要实现如下功能:在MusicActivity中写入文件Environment.getExternalStorageDirectory() + "/IPC_shareFile/usercache",并把MyUser的内容进去,然后在另一个进程的OtherActivity里把usercache的内容读出来,并且展示到TextView中。
好嘞,马上开始
1、在 MusicActivity 中的写文件:
主要代码如下
/**
* 作者:created by yufenfen on 2019/3/28:18:16
* 邮箱: [email protected]
*/
public class MusicActivity extends Activity {
private static final String TAG = "MusicActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.myprocesses_activity_layout);
ipcWriteFile();
}
...
/*******************************************************/
/************************ 共享文件 ********************/
/*******************************************************/
private void ipcWriteFile(){
new Thread(new Runnable() {
@Override
public void run() {
MyUser user = new MyUser("19870316", "雨纷纷", MyUser.SEX_MAN);
File dir = new File(Environment.getExternalStorageDirectory() + "/IPC_shareFile");
if (!dir.exists()) {
dir.mkdirs();
}
File cachedFile = new File(Environment.getExternalStorageDirectory() + "/IPC_shareFile/usercache");
ObjectOutputStream objectOutputStream = null;
try {
if (!cachedFile.exists()) {
cachedFile.createNewFile();
}
objectOutputStream = new ObjectOutputStream(new FileOutputStream(cachedFile));
objectOutputStream.writeObject(user);
} catch (IOException e) {
e.printStackTrace();
} finally {
try{
objectOutputStream.close();
} catch (Exception e){
e.printStackTrace();
}
}
}
}).start();
}
}
2、在 OtherActivity 中读文件
代码如下
/**
* Copyright (C), 2015-2019, 雨纷纷工作室
* FileName: OtherActivity
* Author: yufenfen
* Email: [email protected]
* Date: 2019/4/4 6:37 PM
* Description: IPC共享文件方式读取方
*/
package yb.demo.myProcesses;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import org.w3c.dom.Text;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import yb.demo.yuffdemo.R;
/**
* @ClassName: OtherActivity
* @Description: java类作用描述
* @Author: yufenfen
* @Date: 2019/4/4 6:37 PM
*/
public class OtherActivity extends Activity {
private static final String TAG = "OtherActivity";
private static final int MSG_SHOW = 0;
private TextView mShow;
Messenger mMessenger = new Messenger(new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SHOW:
MyUser user = (MyUser) msg.obj;
mShow.setText(user.toString());
break;
default:
break;
}
}
});
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.other_activity_layout);
mShow = (TextView) findViewById(R.id.showtext);
}
public void ipcReadFile(View view) {
// Toast.makeText(this, "没问题", Toast.LENGTH_SHORT).show();
new Thread(new Runnable() {
@Override
public void run() {
MyUser user = null;
File cachedFile = new File(Environment.getExternalStorageDirectory().getPath()+"/IPC_shareFile/usercache");
if (cachedFile.exists()) {
ObjectInputStream objectInputStream = null;
try {
objectInputStream = new ObjectInputStream(new FileInputStream(cachedFile));
user = (MyUser) objectInputStream.readObject();
Message msg = Message.obtain(null, MusicService.MSG_C2S_SING, user);
mMessenger.send(msg);
Log.d(TAG, "recover user:" + user);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch(RemoteException e){
e.printStackTrace();
} finally {
try{
objectInputStream.close();
} catch (Exception e){
e.printStackTrace();
}
}
}
}
}).start();
}
}
注意:
在 SecondActivity 中成功从文件中恢复的之前存储的 User 对象是反序列化得到的对象,虽然内容跟之前的 User 对象一样,但跟序列化之前的 User 对象并不是同一个对象实体,本质上还是两个对象。
三、SharedPreferences
当然,SharedPreferences 是个特例,它是 Android 中提供的轻量级存储方案,它通过键值对的方式来存储数据,在底层实现上采用 XML 文件来存储键值对,每个应用的 SharedPreferences 文件都可以在当前包所在的 data 目录下查看到。一般来说,它的目录位于 /data/data/ 包名 /shared_prefs 目录下。
从本质上来说,SharedPreferences 也属于文件的一种,但是由于系统对它的读/写有一定的缓存策略,即在内存中会有一份 SharedPreferences 文件的缓存,因此在多进程模式下,系统对它的读/写就变得不可靠,当面对高并发的读/写访问,SharedPreferences 有很大几率丢失数据,因此,不建议在进程间通信中使用 SharedPreferences 。
四、总结
通过文件共享这种方式来共享数据对文件格式是没有具体要求的,比如可以是文本文件,也可以是 XML 文件,只要读/写双方约定数据格式即可。通过文件共享的方式也是有局限性的,比如并发读/写的问题,因此我们要尽量避免并发读/写情况的发生或者考虑使用线程同步来限制多个线程的读/写操作。
通过上面分析,我们知道,文件共享方式适合在对数据同步要求不高的进程之间进行通信,并且要妥善处理并发读/写的问题。