Android中的状态保存和恢复, 包括Activity和Fragment以及其中View的状态处理.
Activity的状态除了其中的View和Fragment的状态之外, 还需要用户手动保存一些成员变量.
Fragment的状态有它自己的实例状态和其中的View状态, 因为其生命周期的灵活性和实际需要的不同, 情况会多一些.
根据源码, 列出了Fragment中实例状态和View状态保存和恢复的几个入口, 便于分析查看.
最后专门讲了WebView状态保存和恢复, 问题及处理.
还有一个工具类icepick的介绍.
作为热身, 先来讲一下Activity的状态保存和恢复.
什么时候需要恢复Activity
关于Activity的销毁和重建, 之前有这么一篇博文: Activity的重新创建
总结来说, 就是Activity的销毁, 分为彻底销毁和留下数据的销毁两种.
彻底销毁是指用户主动去关闭或退出这个Activity. 此时是不需要状态恢复的, 因为下次回来又是重新创建全新的实例.
留下数据的销毁是指系统销毁了activity, 但是当用户返回来时, 会重新创建它, 让用户觉得它一直都在.
屏幕旋转重建可以归结为第二种情况, 打开Do not keep activities开关, 切换activities也是会出现第二种情况.
打开Do not keep activities开关就是为了模拟内存不足时的系统行为, 这里有一篇分析
如何恢复
实际上系统已经帮我们做好了View层面基本的恢复工作, 主要是依靠下面两个方法:
@OverrideprotectedvoidonSaveInstanceState(Bundle outState){ super.onSaveInstanceState(outState); // 在onStop()之前调用, 文档中说并不保证在onPause()的之前还是之后// 我的试验中一般是在onPause()之后 } @OverrideprotectedvoidonRestoreInstanceState(Bundle savedInstanceState){ super.onRestoreInstanceState(savedInstanceState); // 在onStart() 之后 }
Bundle其中包含了activity中的view和fragment的各种信息, 所以调用基类的方法就可以完成基本的view层面的恢复工作.
注意这两个方法并不是activity的生命周期回调, 对于activity来说它们不是一定会发生的.
另外需要注意的是, View必须要有id才能被恢复.
举一个实例来说明:
Activity A start B, 那么A的onSaveInstanceState()
会在onStop()之前调用, 以防A被系统销毁.
但是在B中按下back键finish()了自己后, B被销毁的过程中, 并没有调用onSaveInstanceState()
, 是因为B并没有被压入task的back stack中,
也即系统知道B并不需要储存自己的状态.
正常情况下, 返回到A, A没有被销毁, 也不会调用onRestoreInstanceState()
, 因为所有的状态都还在, 并不需要重建.
如果我们打开了Do not keep activities开关, 模拟系统内存不足时的行为, 从A到B, 可以看到当B resume的时候A会一路走到onDestroy(),
而关掉B之后, A会从onCreate()开始走, 此时onCreate()的参数bundle就不为空了, onStart()之后会调用onRestoreInstanceState()
方法, 其参数bundle中内容类似于如下:
Bundle[{android:viewHierarchyState=Bundle[mParcelledData.dataSize=272]}]
其中包含了View的状态, 如果有Fragment, 也会包含Fragment的状态, 其实质是保存了FragmentManagerState, 内容类似于如下:
Bundle[{
android:viewHierarchyState=Bundle[{
android:views={
16908290=android.view.AbsSavedState$1@bc382e7, 2131492950=CompoundButton.SavedState{
4034f96 checked=true}, 2131492951=android.view.AbsSavedState$1@bc382e7}}], android:fragments=android.app.FragmentManagerState@bacc717}]
对于上面的例子来说, B什么时候会调用onSaveInstanceState()
呢?
当从A打开B之后, 按下Home键, B就会调用onSaveInstanceState()
.
因为这时候系统不知道用户什么时候会返回, 有可能会把B也销毁了, 所以保存一下它的状态.
如果下次回来它没有被重建, onRestoreInstanceState()
就不会被调用, 如果它被重建了, onRestoreInstanceState()
才会被调用.
Activity保存方法的调用时机
activity的onSaveInstanceState()
和onRestoreInstanceState()
方法在如下情形下会调用:
- 屏幕旋转重建: 先save再restore.
- 启动另一个activity: 当前activity在离开前会save, 返回时如果因为被系统杀死需要重建, 则会从onCreate()重新开始生命周期, 调用onRestoreInstanceState(