在设备运行中发生配置变更时,如设备旋转,需采用某种方式保存以前的数据。覆盖以下Activity
方法就是一种实现方式:
protected void onSaveInstanceState(Bundle outState)
该方法通常在onPause()
、onStop()
以及onDestroy()
方法之前由系统调用。
方法onSaveInstanceState(...)
默认的实现要求所有activity的视图将自身状态数据保存在Bundle
对象中。Bundle
是存储字符串键与限定类型值之间映射关系(键-值对)的一种结构。
之前已使用过Bundle
,如下列代码所示,它作为参数传入onCreate(Bundle)
方法:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
覆盖onCreate(...)
方法时,我们实际是在调用activity超类的onCreate(...)
方法,并传入收到的bundle。在超类代码实现里,通过取出保存的视图状态数据,activity的视图层级结构得以重新创建。
覆盖onSaveInstanceState(Bundle)
方法
可通过覆盖 onSaveInstanceState(...)
方法,将一些数据保存在 Bundle 中,然后在onCreate(...)
方法中取回这些数据。设备旋转时,将采用这种方式保存mCurrentIndex
变量值。
首先,打开QuizActivity.java文件,新增一个常量作为将要存储在bundle中的键-值对的键,如代码清单3-5所示。
代码清单3-5 新增键?值对的键(QuizActivity.java)
public class QuizActivity extends Activity {
private static final String TAG = "QuizActivity";
private static final String KEY_INDEX = "index";
Button mTrueButton;
...
然后,覆盖onSaveInstanceState(...)
方法,以刚才新增的常量值作为键,将mCurrentIndex
变量值保存到Bundle中,如代码清单3-6所示。
代码清单3-6 覆盖
onSaveInstanceState(...)
方法(QuizActivity.java)
mNextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCurrentIndex = (mCurrentIndex + 1) % mAnswerKey.length;
updateQuestion();
}
});
updateQuestion();
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
Log.i(TAG, "onSaveInstanceState");
savedInstanceState.putInt(KEY_INDEX, mCurrentIndex);
}
最后,在onCreate(...)
方法中查看是否获取了该数值。如确认获取成功,则将它赋值给变量mCurrentIndex
,如代码清单3-7所示。
代码清单3-7 在
onCreate(...)
方法中检查存储的bundle信息(QuizActivity.java)
...
if (savedInstanceState != null) {
mCurrentIndex = savedInstanceState.getInt(KEY_INDEX, 0);
}
updateQuestion();
}
运行GeoQuiz应用。单击下一步按钮。现在,无论设备自动或手动旋转多少次,新创建的QuizActivity
都将会记住当前正在回答的题目。
注意,我们在Bundle
中存储和恢复的数据类型只能是基本数据类型(primitive type)以及可以实现Serializable
接口的对象。创建自己的定制类时,如需在onSaveInstanceState(...)
方法中保存类对象,记得实现Serializable
接口。
测试onSaveInstanceState(...)
的实现是个好习惯,尤其在需要存储和恢复对象时。设备旋转很容易测试,但测试低内存状态就困难多了。比如Android为回收内存而销毁activity的场景。
覆盖onSaveInstanceState(...)
方法并不仅仅用于处理设备旋转相关的问题。用户离开当前acitivity管理的用户界面,或Android需要回收内存时,activity也会被销毁。
不过Android从不会为了回收内存,而去销毁正在运行的activity。activity只有在暂停或停止状态下才可能会被销毁。此时,会调用onSaveInstanceState(...)
方法。
调用onSaveInstanceState(...)
方法时,用户数据随即被保存在Bundle
对象中。然后操作系统将Bundle
对象放入activity记录中。
为便于理解activity记录,我们增加一个暂存状态(stashed state)到activity生命周期,如图3-14。
图3-14 完整的activity生命周期
activity暂存后,Activity
对象不再存在,但操作系统会将activity记录对象保存起来。这样,在需要恢复activity时,操作系统可以使用暂存的activity记录重新激活activity。
注意,activity进入暂存状态并不一定需要调用onDestroy()
方法。不过,onPause()
和onSaveInstanceState(...)
通常是我们需要调用的两个方法。常见的做法是,覆盖onSaveInstanceState(...)
方法,将数据暂存到Bundle
对象中,覆盖onPause()
方法处理其他需要处理的事情。
有时,Android不仅会销毁activity,还会彻底停止当前应用的进程。不过,只有在用户离开当前应用时才会发生这种情况。即使这种情况真的发生了,暂存的activity记录依然被系统保留着,以便于用户返回应用时activity的快速恢复。
那么暂存的activity记录到底可以保留多久?前面说过,用户按了后退键后,系统会彻底销毁当前的activity。此时,暂存的activity记录同时被清除。此外,系统重启或长时间不使用activity时,暂存的activity记录通常也会被清除。