get(index);
fragment.setInitialSavedState(savedState);
}
注意这里用了SparseArray来存储Fragment的状态, 并且加上了@State
, 这样在Activity重建的时候其中的内容也能够被恢复.
Back stack中的fragment
有一点很特殊的是, 当Fragment从back stack中返回, 实际上是经历了一次View的销毁和重建, 但是它本身并没有被重建.
即View状态需要重建, 实例状态不需要重建.
举个例子说明这种情形: Fragment被另一个Fragment replace(), 并且压入back stack中, 此时它的View是被销毁的, 但是它本身并没有被销毁.
也即, 它走到了onDestroyView(), 却没有走onDestroy()
和onDetact()
.
等back回来的时候, 它的view会被重建, 重新从onCreateView()开始走生命周期.
在这整个过程中, 该Fragment中的成员变量是保持不变的, 只有View会被重新创建.
在这个过程中, instance state的saving并没有发生.
所以, 很多时候Fragment还需要考虑的是在没有Activity帮助的情形下(Activity并没有可能重建的情形), 自身View状态的保存.
此时要注意一些不容易发现的错误, 比如List的新实例需要重新setAdapter等.
Fragment setRetainInstance
Fragment有一个相关方法:
setRetainInstance
这个方法设置为true的时候表示, 即便activity重建了, 但是fragment的实例并不被重建.
注意此方法只对没有放在back stack中的fragment生效.
什么时候要用这个方法呢? 处理configuration change的时候:
Handling Configuration Changes with Fragments
这样, 当屏幕旋转, Activity重建, 但是其中的fragment和fragment正在执行的任务不必重建.
更多解释可以参见:
http://stackoverflow.com/questions/11182180/understanding-fragments-setretaininstanceboolean
http://stackoverflow.com/questions/11160412/why-use-fragmentsetretaininstanceboolean
注意这个方法只是针对configuration change, 并不影响用户主动关闭和系统销毁的情况:
当activity被用户主动finish, 其中的所有fragments仍然会被销毁.
当activity不在最顶端, memory不够了, 系统仍然可能会销毁activity和其中的fragments.
View的状态保存和恢复
View的状态保存和恢复主要是依赖于下面几个方法:
保存: saveHierarchyState()
-> dispatchSaveInstanceState()
-> onSaveInstanceState()
恢复: restoreHierarchyState()
-> dispatchRestoreInstanceState()
-> onRestoreInstanceState()
还有两个重要的前提条件是View要有id, 并且setSavedEnabled()
为true.(这个值默认为true).
在系统的widget里(比如TextView, EditText, Checkbox等), 这些都是已经被处理好的, 我们只需要给View赋予id, Activity和Fragment重建的时候会自动恢复其中的状态. (这里的Fragment恢复对应入口一和入口三, 入口二属于跨实例新建的情况).
但是如果你要使用第三方的自定义View, 就需要确认一下它们内部是否有状态保存和恢复的代码.
如果不行你就需要继承该自定义View, 然后实现这两个方法:
//
// Assumes that SomeSmartButton is a 3rd Party view that
// View State Saving/Restoring are not implemented internally
//
public class SomeBetterSmartButton extends SomeSmartButton {
...
@Override
public Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
// Save current View's state here
return bundle;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
super.onRestoreInstanceState(state);
// Restore View's state here
}
...
}
WebView的状态保存和恢复
WebView的状态保存和恢复不像其他原生View一样是自动完成的.
WebView不是继承自View的.
如果我们把WebView放在布局里, 不加处理, 那么Activity或Fragment重建的过程中, WebView的状态就会丢失, 变成初始状态.
在Fragment的onSaveInstanceState()里面可以加入如下代码来保存WebView的状态:
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
webView.saveState(outState);
}
然后在初始化的时候, 增加判断, 不必每次都打开初始链接:
if (savedInstanceState != null) {
webView.restoreState(savedInstanceState);
} else {
webView.loadUrl(TEST_URL);
}
这样处理以后, 在重新建立的时候, WebView的状态就能恢复到离开前的页面.
不论WebView是放在Activity里还是Fragment里, 这个方法都适用.
但是Fragment还有另一种情况, 即Fragment被压入back stack, 此时它没有被destroy(), 所以没有调用onSavedInstanceState()这个方法.
这种情况返回的时候, 会从onCreateView()开始, 并且savedInstanceState为null, 于是其中WebView之前的状态在此时丢失了.
解决这种情况可以利用Fragment实例并未销毁的条件, 增加一个成员变量bundle, 保存WebView的状态, 最终解决如下:
private Bundle webViewState;
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
sup