参考资料:

  • 《疯狂 Android 讲义》
  • 《Android 编程权威指南》

生命周期

每个 Activity 实例都有其生命周期。在其生命周期内,activity 在运行、暂停和停止三种可能的状态间进行转换。每次状态发生转换时,都有一个 Activity 方法将状态改变的消息通知给 activity。下图显示了 activity 的生命周期、状态以及状态切换时系统调用的方法。

避免 activity 被销毁导致数据丢失

请记住,只要在应用运行中设备配置发生了改变,Android 就会销毁当前 activity ,然后再新建一个 activity。因此,在设备运行中发生配置变更时,如设备旋转,需采用某种方式保存以前的数据。覆盖以下 Activity 方法就是一种实现方式:

1
protected void onSaveInstanceState(Bundle outState)

该方法通常在 onPause()onStop() 以及 onDestroy() 方法之前由系统调用。

方法 onSaveInstanceState(...) 默认的实现要求所有 activity 的视图将自身状态数据保存在 Bundle 对象中。Bundle 是存储字符串键与限定类型值之间映射关系(键-值对)的一种结构。

1
2
3
4
@Override
public void OnCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...

覆盖 onCreate(...) 方法时,我们实际是在调用 activity 超类的 onCreate(...) 方法,并传入收到的 bundle 。在超类代码实现里,通过取出保存的视图状态数据,activity 的视图层级结构得以重新创建。

覆盖 onSaveInstanceState(Bundle) 方法

可通过覆盖 onSaveInstanceState(...) 方法,将一些数据保存在 Bundle 中,然后在 onCreate(...) 方法中取回这些数据。当设备运行中发生配置变更时,将采用这种方式保存以前的数据。示例:

1
2
3
4
5
6
7
public class MyActivity extends Activity {
private static final String TAG = "MyActivity";
private static final String KEY_INDEX = "index";
Button mTrueButton;
...
}

然后,覆盖 onSaveInstanceState(...) 方法,以刚才新增的常量值作为键,将变量值保存到 Bundle 中。示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quiz);
...
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
Log.i(TAG, "onSaveInstanceState");
savedInstanceState.putInt(KEY_INDEX, mMyVariable);
}

最后,在 onCreate(...) 方法中查看是否获取了该数值。如确认获取成功,则将它赋值给变量。示例:

1
2
3
4
...
if (savedInstanceState != null) {
mMyVariable = savedInstanceState.getInt(KEY_INDEX, 0);
}

注意,我们在 Bundle 中存储和恢复的数据类型只能是基本数据类型(primitive type)以及可以实现 Serializable 接口的对象。创建自己的定制类时,如需在 onSaveInstanceState(...) 方法中保存类对象,记得实现 Serializable 接口。

Tips:测试 onSaveInstanceState(...) 的实现是个好习惯,尤其在需要存储和恢复对象时。测试方法见这里

再探 Activity 生命周期

覆盖 onSaveInstanceState(...) 方法并不仅仅用于处理设备旋转相关的问题。用户离开当前 acitivity 管理的用户界面,或 Android 需要回收内存时,在暂停或状态下的 activity 也会被销毁。此时,会调用 onSaveInstanceState(...) 方法。

调用 onSaveInstanceState(...) 方法时,用户数据随即被保存在 Bundle 对象中。然后操作系统将 Bundle 对象放入 activity 记录中。为便于理解 activity 记录,我们增加一个暂存状态(stashed state)到 activity 生命周期,如下图所示:

activity 暂存后,Activity 对象不再存在,但操作系统会将 activity 记录对象保存起来。这样,在需要恢复 activity 时,操作系统可以使用暂存的 activity 记录重新激活 activity。

onPause()onSaveInstanceState(...) 通常是我们需要调用的两个方法。常见的做法是,覆盖 onSaveInstanceState(...) 方法,将数据暂存到 Bundle 对象中,覆盖 onPause() 方法处理其他需要处理的事情。

有时,Android 不仅会销毁 activity,还会彻底停止当前应用的进程。不过,只有在用户离开当前应用时才会发生这种情况。即使这种情况真的发生了,暂存的 activity 记录依然被系统保留着,以便于用户返回应用时 activity 的快速恢复。不过,当用户按了后退键后,系统会彻底销毁当前的 activity。此时,暂存的 activity 记录同时被清除。此外,系统重启或长时间不使用 activity 时,暂存的 activity 记录通常也会被清除。