前言
安卓应用中一个activity的销毁大致分为两种,正常销毁和异常销毁。
正常销毁比如:当用户按 返回按钮或您的Activity通过调用 finish()示意自己的销毁,
异常销毁通俗的说就是指未经过你同意的销毁,比如:
1、当用户按下HOME键时:此时如果按下HOME键后,打开了很多其他的应用程序,那么在前台应用需要更多内存的情况下,该Activity就有可能被系统销毁。
2,长按HOME键,选择运行其他的程序时。
3,按下电源按键(关闭屏幕显示)时。
4,从activity A中启动一个新的activity时。
5,屏幕方向切换时,例如从竖屏切换到横屏时。
问题:当activity因为系统原因而被销毁时,如果之后这个activity重新回到前台, 之前所作的改变就会消失,为了避免此种情况的发生,我们就需要进行activity的状态保存和回复操作。
onSaveInstanceState进行状态保存
所有的异常销毁都会触发onSaveInstanceState方法的调用,该方法中包含一个Bundle类型的参数,默认情况下,系统会使用 Bundle 实例状态保存您的Activity布局(比如,输入到 EditText 对象中的文本值)中有关每个 View 对象的信息。 这样,如果您的Activity实例被销毁并重新创建,布局状态便恢复为其先前的状态,且您无需书写任何代码。 但是,如果您要保存有关Activity状态的其他数据,比如恢复activity中的某个成员变量在上次异常销毁时的状态值,那您就必须重写 onSaveInstanceState() 回调方法,手动进行相应值的保存,这样当activity被重新创建时,它会将相同的 Bundle 对象同时传递给 onRestoreInstanceState() 和onCreate() 方法。
如果您要保存有关Activity状态的其他数据,您必须实现 onSaveInstanceState() 并将键值对添加至 Bundle 对象。 例如:
注意 Activity onSaveInstanceState() 有2个重载方法,一般我们使用下面的带一个参数的即可:
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// 当activity被异常销毁时,保存成员变量isCheck的值
outState.putBoolean("isCheck",isCheck);
}
总而言之:onSaveInstanceState的调用遵循一个重要原则,即当系统“未经你许可”时销毁了你的activity,则onSaveInstanceState会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据(当然你不保存那就随便你了)。
注意:由于onSaveInstanceState()方法不一定会被调用.,比如用户按下BACK键退出activity时, 用户显然想要关闭这个activity, 因此不适合在该方法中保存持久化数据, 例如向数据库中插入记录等. 保存持久化数据的操作应该放在onPause()中. onSaveInstanceState()方法只适合保存瞬态数据, 比如UI控件的状态, 成员变量的值等。
获取activity被异常销毁时保存的状态值
当您的Activity在先前销毁之后重新创建时,您可以从系统向Activity传递的 Bundle 恢复已保存的状态。onCreate() 和 onRestoreInstanceState() 回调方法均接收包含实例状态信息的相同 Bundle。
因为无论系统正在创建Activity的新实例还是重新创建先前的实例,都会调用 onCreate() 方法,因此您必须在尝试读取它之前检查状态 Bundle 是否为 null。 如果为 null,则系统将创建Activity的新实例,而不是恢复已销毁的先前实例。
例如,此处显示您如何可以在 onCreate() 中恢复一些状态数据:
@Override
protected void initState(Bundle savedInstanceState) {
if(savedInstanceState != null){
isCheck = savedInstanceState.getBoolean("isCheck",false);
}
}
因为用到了BaseActivity,上面的方法是继承了BaseActivity的子类中调用的,BaseActivity如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ...
// 子类中需要获取Activity异常销毁时的值的时候使用
initState(savedInstanceState);
// 初始化页面
initView();
// ...
}
如果没有用到BaseActivity,那就很简单了,直接在Activity中的onCreate方法中书写即可:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ...
if(savedInstanceState != null){
isCheck = savedInstanceState.getBoolean("isCheck",false);
}
// ...
}
当然您也可以选择实现系统在 onStart() 方法之后调用的 onRestoreInstanceState() ,而不是在onCreate() 期间恢复状态。 系统只在存在要恢复的已保存状态时调用 onRestoreInstanceState() ,因此您无需检查 Bundle 是否为 null:
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
isCheck = savedInstanceState.getBoolean("isCheck",false);
}
至于onRestoreInstanceState方法,需要注意的是,onSaveInstanceState方法和onRestoreInstanceState方法“不一定”是成对的被调用的,onRestoreInstanceState被调用的前提是,activity A“确实”被系统销毁了,而如果仅仅是停留在有这种可能性的情况下,则该方法不会被调用,例如,当正在显示activity A的时候,用户按下HOME键回到主界面,然后用户紧接着又返回到activity A,这种情况下activity A一般不会因为内存的原因被系统销毁,故activity A的onRestoreInstanceState方法不会被执行
另外,onRestoreInstanceState的bundle参数也会传递到onCreate方法中,你也可以选择在onCreate方法中做数据还原
Fragment的状态保存和恢复
相对于Activity,Fragment的情况,就显得特别复杂,如果有嵌套Fragment,则更复杂了。如果页面不是特别复杂,能不用嵌套fragment,则不用;