浅谈 Android 中的 Bundle

文章目录
  1. 1. 简介 Bundle
  2. 2. 演示 Demo
  3. 3. 深入探讨
  4. 4. 参考文献

博观而约取,厚积而薄发。

说起 Bundle,在 Android 开发中再常见不过了,几乎自接触 Android 第一天开始,每天都会用到。不知读者有没有想过这样一个问题,“Android 中为何设计出 Bundle 而不是直接以 Map 结构在 Activity 之间传递数据”,带着问题,下面来对 Bundle 一探究竟。

简介 Bundle

Android 官方文档这么描述的,“A mapping from String keys to various Parcelable values”。大概可以总结出两点:

  • Bundle 主要用于传递数据;
  • Bundle 保存的数据,是以 key-value (键值对) 的形式存在的。

开发中,经常使用 Bundle 在 Activity 之间传递数据,传递的数据可以是 boolean、String 等基本类型或它们对应的数组,也可以是对象或对象数组。注意,Bundle 传递的是对象或对象数组时,必须实现 Serializable 或 Parcelable 接口。

I. 传递基本类型

Bundle 提供了各种常用类型的 putXxx() 和 getXxx() 方法,用于读写基本类型的数据。具体 API 使用见官网,举一个简单的示例如下:

写数据

1
2
3
4
5
6
7
8
Intent intent = new Intent();

Bundle bundle = new Bundle();
bundle.putString("name", "Iamasoldier6");
bundle.putInt("height", 176);

intent.putExtras(bundle);
startActivity(intent);

读数据

1
2
3
4
Bundle bundle = getIntent().getExtras();

String name = bundle.getString("name");
int height = bundle.getInt("height");

II. 传递对象

传递的对象分为必须实现 Parcelable 接口和 Serializable 接口。简单说明如下:

Parcelable 接口,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public interface Parcelable {

// 描述 Parcelable 实例中特定的对象种类
public int describeContents();

// 对象写入 Parcel 中,即打包需要传递的数据到 Parcel 容器中保存,以便从 Parcel 中获取数据
public void writeToParcel(Parcel dest, int flags);

// 必须实现的接口,提供公共的 CREATOR 域,生成来自 Parcel 的 Parcelable 类的实例
public interface Creator<T> {

// 创建一个 Parcelable 类的实例,其初始化来源于给定的 data 被写入的 Parcel,通俗点说,
// 即从 Parcel 容器中读取传递的数据值,封装成 Parcelable 对象返回至逻辑层
public T createFromParcel(Parcel source);

// 创建一个 Parcelable 类的新数组,即供外部类反序列化本类的数组
public T[] newArray(int size);
}
}

Serializable 接口,

1
2
3
public interface Serializable {

}

显然,是一个空接口,没什么具体内容,目的只是简单地标识一个类的对象可以被序列化。

演示 Demo

Demo 地址:BundleDemo

首先,看 MainActivity:

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

private static final String TAG = "MAIN_ACTIVITY";

private Button mBtnBasic;
private Button mBtnParcelable;
private Button mBtnSerializable;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

mBtnBasic = (Button) findViewById(R.id.btn_basic);
mBtnParcelable = (Button) findViewById(R.id.btn_parcelable);
mBtnSerializable = (Button) findViewById(R.id.btn_serializable);

mBtnBasic.setOnClickListener(this);
mBtnParcelable.setOnClickListener(this);
mBtnSerializable.setOnClickListener(this);
}

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_basic:
sendBasicBundleData();
break;
case R.id.btn_parcelable:
sendParcelableBundleData();
break;
case R.id.btn_serializable:
sendSerializableBundleData();
break;
default:
break;
}
}

private void sendBasicBundleData() {
Intent intent = new Intent(this, ReceiveActivity.class);

Bundle bundle = new Bundle();
bundle.putString("name", "Iamasoldier6");
bundle.putInt("height", 176);
intent.putExtras(bundle);

startActivity(intent);
}

private void sendParcelableBundleData() {
Intent intent = new Intent(this, ReceiveActivity.class);

Book book = new Book();
book.setBookName("Android");
book.setAuthor("Iamasoldier6");
book.setPublishTime(2017);

Bundle bundle = new Bundle();
bundle.putParcelable("parcelable", book);
intent.putExtras(bundle);

startActivity(intent);
}

private void sendSerializableBundleData() {
Intent intent = new Intent(this, ReceiveActivity.class);

Person person = new Person();
person.setName("Iamasoldier6");
person.setAge(24);

Bundle bundle = new Bundle();
bundle.putSerializable("serializable", person);
intent.putExtras(bundle);

startActivity(intent);
}

}

很简单,主界面放置了 3 个按钮,点击分别传递不同类型的数据:

ReceiveActivity:

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
public class ReceiveActivity extends AppCompatActivity implements View.OnClickListener {

private static final String TAG = "RECEIVE_ACTIVITY";

private Button mBtnBack;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_receive);

mBtnBack = (Button) findViewById(R.id.btn_back);
mBtnBack.setOnClickListener(this);

receiveBasicData();
receiveParcelableData();
receiveSeriableData();
}

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_back: {
finish();
break;
}
default:
break;
}
}

private void receiveBasicData() {
Bundle bundle = getIntent().getExtras();

String name = bundle.getString("name");
int height = bundle.getInt("height");

if (name != null && height != 0) {
Log.d(TAG, "Receive basic data -- " + "name = " + name + ", height = " + height);
}
}

private void receiveParcelableData() {
Book book = (Book) getIntent().getParcelableExtra("parcelable");

if (book != null) {
Log.d(TAG, "Receive parcelable data -- "
+ "Book name is: " + book.getBookName() + ", "
+ "Author is: " + book.getAuthor() + ", "
+ "Publish time is: " + book.getPublishTime());
}
}

private void receiveSeriableData() {
Person person = (Person) getIntent().getSerializableExtra("serializable");

if (person != null) {
Log.d(TAG, "Receive serializable data -- "
+ "The name is: " + person.getName() + ", "
+ "The age is: " + person.getAge());
}
}

}

该页就放置了一个返回按钮,点击返回主界面:

主界面中,从上至下点击对应的按钮,跳至 Receive 界面,打印出的 Log 分别如下:

1
2
3
08-26 04:36:22.481 1894-1894/com.iamasoldier6.bundledemo D/RECEIVE_ACTIVITY: Receive basic data -- name = Iamasoldier6, height = 176
08-26 04:36:48.010 1894-1894/com.iamasoldier6.bundledemo D/RECEIVE_ACTIVITY: Receive parcelable data -- Book name is: Android, Author is: Iamasoldier6, Publish time is: 2017
08-26 04:37:12.558 1894-1894/com.iamasoldier6.bundledemo D/RECEIVE_ACTIVITY: Receive serializable data -- The name is: Iamasoldier6, The age is: 24

深入探讨

使用 Bundle,在 Intent 中不要直接传递较大的数据,若有此要求,则可以考虑静态传递,如文件或共享内存等。

现在,着重回答文章开头提出的问题:

事实上,翻看 Bundle 的源码,可以发现其内部是由 ArrayMap 实现的。更进一步,ArrayMap 的内部实现是两个数组,其中,一个 int 数组存储对象数据对应的下标,一个对象数组保存 key 和 value,其内部使用二分法对 key 进行排序。故在添加、删除和查找数据的时候,均使用二分法查找。注意,仅适用于小数据量操作,若数据量较大的情况下,其性能将退化。对比来看,HashMap 内部是数组和链表结构,在数据量较少的时候,HashMap 的 Entry Array 将比 ArrayMap 占用更多的内存。我们知道,Bundle 的使用场景为小数据量,一般在 Activity 之间只传递 10 个以内的数据,相比之下,使用 ArrayMap 保存数据,其操作速度和内存占用上都具有优势。使用 Bundle 来传递数据,可以保证更快的速度和更少的内存消耗。

此外,Android 中使用 Intent 传递数据,要求数据为基本类型或可序列化类型。HashMap 使用的是 Serializable 进行序列化,使用起来简单,但是开销很大,序列化和反序列化过程需要大量的 I/O 操作;而 Bundle 使用的是 Parcelable 进行序列化,是 Android 平台推荐的序列化方式,虽然使用起来麻烦些,但是开销更小,效率很高。为了更快速地进行数据的序列化和反序列化,Android 系统封装了 Bundle 类,供传输较少量数据的时候使用。

至此,关于 Android 中 Bundle 的简单探讨到此结束。

本人才疏学浅,如有疏漏错误之处,望读者中有识之士不吝赐教,谢谢。

1
Email: [email protected] / WeChat: Wolverine623

您也可以关注我个人的微信公众号码农六哥第一时间获得博客的更新通知,或后台留言与我交流

参考文献

1.https://developer.android.com/reference/android/os/Bundle.html