Android之使用文件进行IPC
一、文件进行IPC介绍
共享文件也是一种不错的进程间通信方式,两个进程通过读/写同一个文件来交换数据。在Windows上,一个文件如果被加了排斥锁将会导致其他线程无法对其进行访问,包括读写,而由于Android系统基于Linux,使其并发读/写文件可以没有限制地进行,甚至两个线程同时对同一个文件进行读写操作是允许的,尽管这可能出现问题。通过文件交换数据很好使用,除了可以交换一些文本信息外,还可以序列化一个对象到文件系统中的同时从另一个进程中恢复这个对象。
二、使用方法
1.数据类实现Parcelable或Serializable接口
public class User implements Parcelable, Serializable { public User() { } public User(int userId, String userName, boolean isMale) { ... } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { ... } public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() { @Override public User createFromParcel(Parcel source) { return ...; } @Override public User[] newArray(int size) { return ...; } }; private User(Parcel in) { ... } @Override public String toString() { return ...; } }
2.序列化一个对象到sd卡上的一个文件里
private void persistToFile() { new Thread(new Runnable() { @Override public void run() { User user = new User(1, "hello world", false); File dir = new File(MyConstants.CHAPTER_2_PATH); if (!dir.exists()) { dir.mkdirs(); } File cachedFile = new File(MyConstants.CACHE_FILE_PATH); ObjectOutputStream objectOutputStream = null; try { objectOutputStream = new ObjectOutputStream(new FileOutputStream(cachedFile)); objectOutputStream.writeObject(user); Log.d(TAG, "persist user:" + user); mTextView.setText("persist user:" + user); } catch (IOException e) { e.printStackTrace(); } finally { MyUtils.close(objectOutputStream); } } }).start(); }
3.在另外的进程中反序列化
private void recoverFromFile(){ new Thread(new Runnable() { @Override public void run() { User user = null; File cachedFile = new File(MyConstants.CACHE_FILE_PATH); if(cachedFile.exists()){ ObjectInputStream objectInputStream = null; try{ objectInputStream = new ObjectInputStream(new FileInputStream(cachedFile)); user = (User)objectInputStream.readObject(); Log.d(TAG,"recover user:"+user); mTextView.setText("recover user:"+user); }catch (IOException e) { e.printStackTrace(); }catch (ClassNotFoundException e){ e.printStackTrace(); }finally { MyUtils.close(objectInputStream); } } } }).start(); }
4.在AndroidManifest.xml中开启多进程
<activity
...
android:process=":remote" />
三、小案例
1.修改activity_main.xml文件
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" tools:context="com.zhangmiao.ipcdemo.MainActivity" android:orientation="vertical" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="File"> </TextView> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="open activity B" /> <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="A"> </TextView> </LinearLayout>
2.添加activity_second.xml文件
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="at activity B" android:layout_gravity="center_horizontal" /> <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Activity B" /> </LinearLayout>
3.添加MyUtils类与MyConstants类(辅助类)
package com.zhangmiao.ipcdemo; import android.app.ActivityManager; import android.content.Context; import java.io.Closeable; import java.io.IOException; import java.util.List; import java.util.concurrent.RecursiveTask; /** * Created by zhangmiao on 2016/12/26. */ public class MyUtils { public static void close(Closeable closeable) { try { if (closeable != null) { closeable.close(); } } catch (IOException e) { e.printStackTrace(); } } }
package com.zhangmiao.ipcdemo; import android.os.Environment; /** * Created by zhangmiao on 2016/12/26. */ public class MyConstants { public static final String CHAPTER_2_PATH = Environment.getExternalStorageDirectory().getPath() + "/zhangmiao/charpter_2/"; public static final String CACHE_FILE_PATH = CHAPTER_2_PATH + "usercache"; public static final int MSG_FROM_CLIENT = 0; public static final int MSG_FROM_SERVICE = 1; }
4.添加User类
package com.zhangmiao.ipcdemo; import android.os.Parcel; import android.os.Parcelable; import java.io.Serializable; /** * Created by zhangmiao on 2016/12/26. */ public class User implements Parcelable, Serializable { public static int sUserId = 1; private int userId; private String userName; private Boolean isMale; public User() { } public User(int userId, String userName, boolean isMale) { this.userId = userId; this.userName = userName; this.isMale = isMale; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(userId); dest.writeString(userName); dest.writeInt(isMale ? 1 : 0); } public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() { @Override public User createFromParcel(Parcel source) { return new User(source); } @Override public User[] newArray(int size) { return new User[size]; } }; private User(Parcel in) { userId = in.readInt(); userName = in.readString(); isMale = in.readInt() == 1; } @Override public String toString() { return String.format( "User:{userId:%s, userName:%s, isMale:%s},",userId, userName, isMale ); } }
5.添加SecondActivity类
package com.zhangmiao.ipcdemo; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.widget.TextView; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; /** * Created by zhangmiao on 2016/12/26. */ public class SecondActivity extends Activity { private static final String TAG = "SecondActivity"; private TextView mTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); Log.d(TAG, "onCreate"); mTextView = (TextView)findViewById(R.id.textView1); } @Override protected void onResume() { Log.d(TAG,"onResume"); super.onResume(); User user = (User)getIntent().getSerializableExtra("extra_user"); Log.d(TAG,"user:"+user.toString()); recoverFromFile(); } private void recoverFromFile(){ new Thread(new Runnable() { @Override public void run() { User user = null; File cachedFile = new File(MyConstants.CACHE_FILE_PATH); if(cachedFile.exists()){ ObjectInputStream objectInputStream = null; try{ objectInputStream = new ObjectInputStream(new FileInputStream(cachedFile)); user = (User)objectInputStream.readObject(); Log.d(TAG,"recover user:"+user); mTextView.setText("recover user:"+user); }catch (IOException e) { e.printStackTrace(); }catch (ClassNotFoundException e){ e.printStackTrace(); }finally { MyUtils.close(objectInputStream); } } } }).start(); } }
6.修改MainActivity类
package com.zhangmiao.ipcdemo; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.Serializable; public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private TextView mTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); User.sUserId = 2; findViewById(R.id.button1).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(); intent.setClass(MainActivity.this, SecondActivity.class); User user = new User(0, "jake", true); intent.putExtra("extra_user", (Serializable) user); startActivity(intent); } }); mTextView = (TextView) findViewById(R.id.textView1); } @Override protected void onResume() { super.onResume(); Log.d(TAG, "UserManager.sUserId=" + User.sUserId); super.onStart(); persistToFile(); } private void persistToFile() { new Thread(new Runnable() { @Override public void run() { User user = new User(1, "hello world", false); File dir = new File(MyConstants.CHAPTER_2_PATH); if (!dir.exists()) { dir.mkdirs(); } File cachedFile = new File(MyConstants.CACHE_FILE_PATH); ObjectOutputStream objectOutputStream = null; try { objectOutputStream = new ObjectOutputStream(new FileOutputStream(cachedFile)); objectOutputStream.writeObject(user); Log.d(TAG, "persist user:" + user); mTextView.setText("persist user:" + user); } catch (IOException e) { e.printStackTrace(); } finally { MyUtils.close(objectOutputStream); } } }).start(); } }
7.修改AndroidManifest.xml文件
<?xml version="1.0" encoding="utf-8"?> <manifest
xmlns:android="http://schemas.android.com/apk/res/android" package="com.zhangmiao.ipcdemo"> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" android:label="@string/app_name" android:launchMode="standard" android:theme="@style/AppTheme.NoActionBar"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".SecondActivity" android:configChanges="screenLayout" android:label="@string/app_name" android:process=":remote" /> </application> </manifest>