在Activity被意外销毁的情况下,使用onSaveInstanceState()和onRestoreInstanceState()方法保存Activity的状态,如UI组件的录入内容、滚动列表的滚动条位置等,并在销毁Activity之后,再次启动这个Activity的时候恢复之前保存的数据。
onSaveInstanceState()和onRestoreInstanceState()并不是生命周期方法,它们与生命周期方法被调用的时机也不一样,它们并不一定会被调用。只有当应用遇到意外情况,如:内存不足而被系统销毁、用户直接按Home键、开启了一个新的Activity等的时候,onSaveInstanceState()方法才会被回调。而当用户主动去销毁一个Activity的时候,如:按回退键后退、从任务管理器中关闭应用等情况,onSaveInstanceState()方法都不会被触发。原则上来说,就是被系统自动销毁的Activity的时候,会触发onSaveInstanceState()方法,而用户主动销毁的行为,将不会触发它。onRestoreInstanceState()方法,只有在确定是系统销毁的情况下,才会在下次Activity被启动的时候调用。因为它们的调用时机无法保证,所以onSaveInstanceState()方法只适合用于保存一些临时性的状态,而对于一些重要的数据,推荐使用生命周期方法onPause()方法进行数据持久化保存。
下图诠释了Activity状态保存的过程:
从上图可以看出,Activity状态的保存,调用的是onSaveInstanceState(),而恢复状态的时候,调用的是onCreate()或者onRestoreInstanceState()方法,下面是这三个方法的完整签名:
· void onCreate(Bundle savedInstanceState)
· void onStaveInstanceState(Bundle outState)
· void onRestoreInstanceState(Bundle savedInstanceState)
这三个方法都有一个Bundle的对象参数,这个Bundle对象就是用来保存于恢复数据而存在的,在意外销毁的时候,回调onSaveInstanceState()方法向Bundle对象中写入数据,在意外销毁之后启动Activity的时候,回调onCreate()或者onRestoreInstanceState()方法从Bundle对象中获取数据。Bundle内部维护了一个Map集合,所以可以把它看出一个Map集合来使用。
onSaveInstanceState()和onRestoreInstanceState()方法在Activity的整个生命周期中,并不一定会被调用,它们有不同的调用时机。
对于onSaveInstanceState()而言,只有在系统认为某个Activity因为系统的原因,变的"容易"被系统销毁时,该Activity的onSaveInstanceState()方法才会被回调执行。那么什么情况下是容易被系统销毁呢?不处于当前Task的最上层,不处于当前屏幕显示并可以获得焦点的时候。因为对于Android系统而言,只要不是前台显示的Activity,在系统内存不足的情况下,都认为它们是"容易"并且可以被销毁的。所以总结来说,有如下几种情况:
1. 当用户按下HOME键,退回桌面。
2. 长按HOME键,选择运行其它应用时。
3. 按下电源键,关闭屏幕显示时。
4. 从当前Activity中启动了一个新的Activity时。
5. 屏幕方向被切换时。
对于onRestoreInstanceState()方法而言,回调的前提是当前Activity确实已经被系统自行销毁了。Android系统的运行机制,决定了在内存足够的情况下,默认它是不会主动销毁应用来释放内存空间的,哪怕是用户主动退出应用的情况下,这样是为了下次开启这个应用时,初始化的速度更快,当然用户是可以主动从任务管理器中清理运行的应用以释放内存。所以这样的系统环境下,首先必须保证Activity的onSaveInstanceState()方法被回调,其次必须是被系统主动销毁之后,再此启动这个Activity,onRestoreInstanceState()方法才会被回调使用。
有一种特殊的情况,就是在默认情况下,Android设备的屏幕方向被改变的时候,会先触发onSaveInstanceState()方法伴随着Activity的销毁,然后触发onRestoreInstanceState()方法伴随着Activity的恢复。
基于这个特殊的情况,重写上面的实例,在Activity屏幕方向切换导致Activity被销毁的时候,保存文本框的内容,并在Activity被重新创建的时候恢复文本框的数据。
代码清单:\codes\05\03\SaveActivityStateDemo\src\com\bookdemo\saveactivitystatedemo\MainActivity.java
public class MainActivity extends Activity { private EditText etUsername; private final static String TAG = "main"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.i(TAG, "---onCreate方法被回调---"); etUsername = (EditText) findViewById(R.id.etUsername); } @Override protected void onSaveInstanceState(Bundle outState) { Log.i("main", "---onSaveInstanceState方法被回调---"); outState.putString("username", etUsername.getText().toString()); super.onSaveInstanceState(outState); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { Log.i("main", "---onRestoreInstanceState方法被回调---"); etUsername.setText(savedInstanceState.getString("username")); super.onRestoreInstanceState(savedInstanceState); } @Override protected void onStart() { Log.i(TAG, "---onStart方法被回调---"); super.onStart(); } @Override protected void onRestart() { Log.i(TAG, "---onRestart方法被回调---"); super.onRestart(); } @Override protected void onResume() { Log.i(TAG, "---onResume方法被回调---"); super.onResume(); } @Override protected void onPause() { Log.i(TAG, "---onPause方法被回调---"); super.onPause(); } @Override protected void onStop() { Log.i(TAG, "---onStop方法被回调---"); super.onStop(); } @Override protected void onDestroy() { Log.i(TAG, "---onDestroy方法被回调---"); super.onDestroy(); } }
在模拟器上运行效果如下:
在这个实例中增加了对Activity生命周期的方法,可以查看LogCat输出的日志,观察Activity状态保存与恢复的方法及Activity生命周期方法的调用时机。
从LogCat输出的日志可以看出,onSvaeInstanceState()方法在生命周期方法onPause()与onStop()方法之间被调用,onRestoreInstanceState()方法在生命周期方法onStart()与onResume()方法之间被调用。
如果没有重写onSaveInstanceState()方法,Activity会使用自己维护的onSaveInstanceState()方法对Activity的UI组件状态进行自动保存,并在需要恢复的时候自动恢复。其实这里所谓的保存UI组件状态,也只是调用了UI组件本身的onSaveInstanceState()方法,Android UI组件均继承了View类,同样也都继承了View类的onSaveInstanceState()方法。但是View组件保存自己的状态,必须在这些UI组件被设置了ID属性的前提下。
因为Activity的onSaveInstanceState()方法还具有保存Activity中UI组件状态的功能,所以如果重写了onSaveInstanceState()方法,需要在其中调用父类的onSaveInstanceState()方法。