您的位置:首页 > 移动开发 > Android开发

Android中的IPC方式——使用Bundle和文件共享

2016-06-16 22:31 441 查看
IPC是inter-process communication的缩写,含义是进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程。IPC方式包括Bundle、文件共享、AIDL、Messenger、ContentProvider和Socket等进程间通信的方式。这里主要讲解Bundle和文件共享的方式。

那么什么进程呢,首先进程和线程是很不同的概念,今晨一般只一个执行单元,在PC和移动设备上指一个程序或者一个应用,一个进程可以包含多个线程,因此进程包含线程,进程与线程是包含与被包含的关系。

当然一个应用也可以开启多进程模式,可以通过android:process属性来设置,这样可以为一个应用多申请点内存。但是进程与进程之间的通信问题就随之而来。另外应用与应用之间的通信方式也属于进程间通信,比如,我们平时使用美团团购付款时调用支付宝就属于进程间通信。

当然IPC方式有很多,这篇博文主要讲Bundle和文件共享

一、使用Bundle

四大苏建中的三大组件Activity、Service、Receiver都是支持Intent中传递Bundle数据的,由于Bundle实现了Parcelable接口,所以它可以方便的在不同的进程中传输。所以当我们在一个进程中启动了另外一个进程的Activity、Service、Receiver,我们就可以在Bundle中附加我们需要传输给远程进程的信息并通过Intent发送出去。当然,我们传输的数据必须能够被序列化。

下面演示一个案例,通过android:process在一个应用之间使用Bundle传输数据,定义MainActivity和BundleActivity属于不同的进程,代码如下:

MainActivity

package com.qian.ipc;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnBundle = (Button) findViewById(R.id.btn_bundle);
btnBundle.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
Bundle bundle = new Bundle();
//保存输入的信息
bundle.putString("name", "Bundle test");
<span style="white-space:pre">	</span>    Intent intent=new Intent(MainActivity.this,BundleActivity.class);
intent.putExtras(bundle);
startActivity(intent);

}
});

}

}


BundleActivity

package com.qian.ipc;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.EditText;

public class BundleActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bundle);
EditText etBundle = (EditText) findViewById(R.id.et_bundle);
Bundle b=getIntent().getExtras();
//获取Bundle的信息
String info=b.getString("name");
etBundle.setText(info);
}

}


Androidmanifest,注意使用了android:process=":remote"

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.qian.ipc"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" />

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.qian.ipc.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.qian.ipc.BundleActivity"
android:label="@string/title_activity_bundle"
android:process=":remote" >
</activity>

</application>

</manifest>


代码很简单,在MainActivity中通过Intent添加Bundle数据并启动BundleActivity,并显示在BundleActivity的EditText中,点击MainActivity中的按钮,页面跳转,数据显示结果如下:



显示的Bundle Test是从MainActivity中跨进程传递过来的。可见通过Bundle可以实现IPC。

二、使用文件共享

共享文件也是一种不错的IPC方式,两个进程通过读写同一个文件来交换数据,比如A进程把数据写入文件,B进程通过读取文件来获取数据。由于Android基于Linux,所以并发读写没有什么限制,甚至线程同时读写文件都可以,尽管会出现问题。利用这个思想,我们可以序列化一个对象到文件系统中,同时另一个进程中回复这个对象。

还是以上一个工程为例:

在MainActivity添加以下代码:

@Override
protected void onResume() {
persistToFile();
super.onStart();
}

private void persistToFile() {
new Thread(new Runnable() {

@Override
public void run() {
String path = Environment.getExternalStorageDirectory().getPath()
+ "/qian/";
User user = new User(1, "hello world", false);
File dir = new File(path);
if (!dir.exists()) {
dir.mkdirs();
}
File cachedFile = new File(path + "cache");
ObjectOutputStream objectOutputStream = null;
try {
objectOutputStream = new ObjectOutputStream(
new FileOutputStream(cachedFile));
objectOutputStream.writeObject(user);
Log.i(TAG, "persist user:" + user);
} catch (IOException e) {
e.printStackTrace();
} finally {

try {
if(objectOutputStream != null)
objectOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}

}
}
}).start();
}

在BundleActivty添加如下代码:

@Override
protected void onResume() {
super.onResume();
// User user = (User) getIntent().getSerializableExtra("extra_user");
Log.d(TAG, "onResume");
recoverFromFile();
}

private void recoverFromFile() {
new Thread(new Runnable() {

@Override
public void run() {
User user = null;
String path = Environment.getExternalStorageDirectory().getPath()
+ "/qian/";
File cachedFile = new File(path + "cache");
if (cachedFile.exists()) {
ObjectInputStream objectInputStream = null;
try {
objectInputStream = new ObjectInputStream(
new FileInputStream(cachedFile));
user = (User) objectInputStream.readObject();
Log.i(TAG, "recover user:" + user);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if(objectInputStream != null)
objectInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}).start();
}

其实一个序列化与反序列化的过程,其中定义了一个可序列化的对象User,继承自Parcelable, Serializable接口,这样便可以序列化,User代码如下:

package com.qian.ipc;

import java.io.Serializable;

import android.os.Parcel;
import android.os.Parcelable;

public class User implements Parcelable, Serializable {
private static final long serialVersionUID = 519067123721295773L;

public int userId;
public String userName;
public boolean isMale;

public Book book;

public User() {
}

public User(int userId, String userName, boolean isMale) {
this.userId = userId;
this.userName = userName;
this.isMale = isMale;
}

public int describeContents() {
return 0;
}

public void writeToParcel(Parcel out, int flags) {
out.writeInt(userId);
out.writeString(userName);
out.writeInt(isMale ? 1 : 0);
out.writeParcelable(book, 0);
}

public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() {
public User createFromParcel(Parcel in) {
return new User(in);
}

public User[] newArray(int size) {
return new User[size];
}
};

private User(Parcel in) {
userId = in.readInt();
userName = in.readString();
isMale = in.readInt() == 1;
book = in
.readParcelable(Thread.currentThread().getContextClassLoader());
}

@Override
public String toString() {
return String.format(
"User:{userId:%s, userName:%s, isMale:%s}, with child:{%s}",
userId, userName, isMale, book);
}

}

最终打印的Log如下:只要红色框圈住的,其中红色框中第二栏Application打印的是进程名,可以看出MainActivity和BundleActivity是属于不同进程的。很显然,BundleActivity成功的从文件中回复了之前存储的User对象的内容,这里之所以说内容,是因为反序列化得到的对象只是在内容上和序列化之前的对象式样的,但他们本质上是两个对象。



通过文件共享这种方式来共享数据对文件格式是没有具体要求的,比如可以是文本文件,也可以是xml文件,只要读写双方约定数据格式即可,也不一定要用序列化对象这种方式来实现文件共享,但是问题是要处理并发读写的问题。

另外SharePreferences是个特例,它是Android中提供的轻量级存储方案,通过键值对的方式存储数据,在底层实现上采用xml文件来存储键值对。但是SharePreferences也有一个问题,但是由于系统对SharePreferences的读写有一定的缓存策略,也就是说在内存中会有一份SharePreferences文件的缓存,因此在多进程模式下,系统对它的读写就变得不可靠,当面对高并发的读写访问SharePreferences有很大几率会丢失数据。

demo源码传至github。

后面更新
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: