在 Android 四大组件中的其中三个(Activity、Service、BroadcastReceiver)都是支持在 Intent 中传递 Bundle 数据的。我们通过 Intent 源码看到,通过 Intent 中的 putExtra() 方法携带 Bundle 数据,然后在另一个组件再通过 getBundleExtra() 方法取出 Bundle 数据:
public Intent putExtra (String name, Bundle value)
public Bundle getBundleExtra (String name)
由于 Bundle 实现了 Parcelable 接口,所以它可以很方便的在不同进程间进行传输:
public final class Bundle extends BaseBundle implements Cloneable, Parcelable { ... }
所以通过上面的介绍我们就可以知道,当我们在一个进程中启动了另一个进程的 Activity 、Service 、 BroadcastReceiver,我们就可以在 Bundle 中附加我们需要传输给远程进程的信息并通过 Intent 对象发送出去。当然,我们传输的数据必须能够被序列化,比如基本类型、实现了 Parcelable 接口的对象、实现了 Serializable 接口的对象以及一些 Android 支持的特殊对象。
下面我们就演示一个 Demo,通过 Bundle 传输一个 int 型数据以及一个实现了 Serializable 接口的对象和一个实现了 Parcelable 接口的对象:
Book.java:
package com.cfm.bundletest;
public class Book implements Parcelable {
private String mName;
private int mPrice;
public Book(String name, int price) {
mName = name;
mPrice = price;
}
protected Book(Parcel in) {
mName = in.readString();
mPrice = in.readInt();
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
public String getName() {
return mName;
}
public int getPrice() {
return mPrice;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mName);
dest.writeInt(mPrice);
}
}
User.java:
package com.cfm.bundletest;
public class User implements Serializable {
public static final long serialVersionUID = 1L;
private String mName;
private String mDream;
public User(String name, String dream) {
mName = name;
mDream = dream;
}
public String getName() {
return mName;
}
public String getDream() {
return mDream;
}
}
Tools.java:
package com.cfm.bundletest;
public class Tools{
// 获取进程名方法
public static String getProcessName(Context context) {
int pid = android.os.Process.myPid();
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> runningApps = am.getRunningAppProcesses();
if (runningApps == null) {
return null;
}
for (ActivityManager.RunningAppProcessInfo procInfo : runningApps) {
if (procInfo.pid == pid) {
return procInfo.processName;
}
}
return null;
}
}
FirstActivity.java:
package com.cfm.bundletest;
public class FirstActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d("cfmtest", "FirstActivity 所在进程名: " + Tools.getProcessName(this));
Intent intent = new Intent(this, SecondActivity.class);
Bundle bundle = new Bundle();
bundle.putInt("mId", 1);
bundle.putSerializable("mUser", new User("cfm", "open Source"));
bundle.putParcelable("mBook", new Book("If Have a Day", 888));
intent.putExtra("mBundle", bundle);
startActivity(intent);
}
}
SecondActivity.java:
package com.cfm.bundletest;
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Log.d("cfmtest", "SecondActivity 所在进程名: " + Tools.getProcessName(this));
Intent intent = getIntent();
Bundle bundle = intent.getBundleExtra("mBundle");
int id = bundle.getInt("mId");
Log.d("cfmtest", "id: " + id);
User user = (User) bundle.getSerializable("mUser");
Log.d("cfmtest", "userName: " + user.getName() + "userDream: " + user.getDream());
Book book = bundle.getParcelable("mBook");
Log.d("cfmtest", "bookName: " + book.getName() + "bookPrice: " + book.getPrice());
}
}
AndroidManifest.xml:
...
<activity android:name=".SecondActivity"
android:process=":remote">
</activity>
...
Log 打印信息:
com.cfm.bundletest D/cfmtest: FirstActivity 所在进程名: com.cfm.bundletest
com.cfm.bundletest:remote D/cfmtest: SecondActivity 所在进程名: com.cfm.bundletest:remote
com.cfm.bundletest:remote D/cfmtest: id: 1
com.cfm.bundletest:remote D/cfmtest: userName: cfmuserDream: open Source
com.cfm.bundletest:remote D/cfmtest: bookName: If Have a DaybookPrice: 888
上面我们通过对能通过 Bundle 传输的数据进行了测试,假设有一个 A 进程正在进行一个计算,计算完成后它要启动 B 进程的一个组件并把计算结果传递给 B 进程,但是该结果并不支持放入 Bundle 中,所以也就无法通过 intent 来传输,这时如果使用其它 IPC 方式就又略显复杂,那我们该怎么办?
我们可以在 A 进程中通过 Intent 启动 B 进程的一个 Service 组件(比如 IntentService),让 B 进程中的 Service 在后台进行计算后再启动 B 进程中真正要启动的目标组件,由于 Service 也运行在 B 进程中,所以目标组件就可以直接获取了计算结果。所以这种方式的核心思想就是将原本需要在 A 进程中计算的任务转移到了 B 进程的后台 Service 中去执行,这样就成功的避免了进程间通信的问题了,而且只用了很小的代价。
总体来讲,使用 Bundle 完成 IPC,操作起来很简单,缺点就是只能传输 Bundle 支持的数据类型。这种方式适用于不同进程(应用)四大组件间的通信。