Fragment的状态保存和恢复
来源:互联网 发布:编程需要哪些基础知识 编辑:程序博客网 时间:2024/06/03 12:33
前言
我们知道,在activity中,当配置发生改变,比如屏幕方向发生变化时,activity会被销毁,然后重新创建。在activity中有两个方法用于保存和恢复状态,分别是:
@Overrideprotected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState);}@Overrideprotected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState);}
当配置发生变化时,onSavaInstanceState会被调用,我们可以用它的参数outState来保存数据和状态;当activity重建的时候,会调用onRestoreInstanceState,然后利用它的参数savedInstanceState将activity恢复到销毁前的状态。另外在onCreate中也可以用于数据恢复,它的参数savedInstanceState和onRestoreInstanceState是一样的。
同样的google在fragment中也实现了类似的状态保存和恢复功能,以下我们分析下fragment的状态保存和恢复。
fragment的状态保存和恢复
实际上,fragment的状态保存和恢复机制和activity是完全一致的。说明解决方案之前,我们首先应该弄清楚下边的几个问题:
- 什么时候保存状态,什么时候恢复状态
- 保存和恢复什么状态(fragment的状态还是view的状态?)
- setRetainInstance(true)
什么时候保存状态,什么时候恢复状态
当系统认为你的fragment存在被销毁的可能时(不包括用户主动退出fragment导致其被销毁,比如按BACK键后fragment被主动销毁), onSaveInstanceState 就会被调用,给你一个机会来保存状态。以下几种情况可能导致fragment被异常销毁;
- 按HOME键返回桌面时
- 按菜单键回到系统后台,并选择了其他应用时
- 按电源键时
- 屏幕方向切换时
这四种情况中,前三种情况都是因为应用处于后台,根据Android系统的缓存机制,为了保持系统的流畅运行,处于后台的应用有很大的可能被清除,既然应用已经不在了,fragment自然也被销毁了;最后一种情况是由于屏幕方向切换导致配置改变,activity被销毁,fragment也随之被销毁了。
在这些情况下,我们就可以通过 onSaveInstanceState 方法将数据保存到它的参数bundle对象中了。以上触发onSaveInstanceState 的状况和activity完全一致。
有了保存,就应该有恢复。和activity不同的是,fragment没有onRestoreInstanceState方法,但是我们可以在onActivityCreated中恢复数据,它的参数中的bundle对象包含了在异常销毁前保存的数据。
保存和恢复什么状态(fragment的状态还是view的状态?)
在说明这个问题之前,我们应该做两个小实验
- 第一个实验:在fragment中的布局中加入一个EditText,设置宽高和id,不做其他设置,然后运行程序。在横屏的时候,在EditText输入随意的内容,然后横屏,你会发现EditText中的内容依然存在,难道fragment可以自动保存view的状态?
- 第二个实验:在fragment中的布局中加入一个EditText,只设置宽高,不做其他设置,然后运行程序。在横屏的时候,在EditText输入随意的内容,然后横屏,你会发现EditText中的内容不在了,难道fragment又不能保存view的状态了?
第三实验:在fragment的布局中加入一个EditText,一个TextView,一个Button,当点击button的时候,将Edittext的内容赋给TextView,代码如下:
布局文件中三个view<EditTextandroid:id="@+id/editText"android:layout_width="wrap_content"android:layout_height="wrap_content"/><TextViewandroid:id="@+id/show"android:layout_width="wrap_content"android:layout_height="wrap_content"/> <Buttonandroid:id="@+id/btn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="按钮"/>
@Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { Log.d(TAG, ">>>onCreateView: "); View view = inflater.inflate(R.layout.fragment_retain, null); final EditText editText = (EditText) view.findViewById(R.id.editText); final TextView textView = (TextView) view.findViewById(R.id.show); Button btn = (Button) view.findViewById(R.id.btn); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (!editText.getText().toString().equals("")) { textView.setText(editText.getText()); } } }); return view; }
横屏的时候输入内容,然后点击按钮,textview被赋值,然后旋转屏幕,textview的内容不见了。(实际情况是,第一次选择的时候内容还在,然后再次旋转回来以后内容就不在了,对于这个现象还没有找到原因)
通过以上的实验,我们发现即使我们没有在onSaveInstanceState 中显示的保存view的状态,但是有时候view的状态还是保存并恢复了,这是怎么回事那?
其实通过前两个实验我们可以看出view的状态是否能被自动保存和id是有关的,通过后两个实验我们发现除了和id有关应该还和其他的设置有关。
原来,在Android中当Activity的onSaveInstanceState调用的时候,Activity会自动收集View层级中每个View的状态。请注意只有在内部实现了View的保存/恢复状态方法的View才会被收集到。一旦onRestoreInstanceState方法被调用,Activity会把收集的数据发送回给View结构树中具有相同android:id配置的View。
这里要注意两点,
第一,view必须已经实现了保存/恢复状态方法
@Overridepublic Parcelable onSaveInstanceState() {Bundle bundle = new Bundle();// Save current View's state herereturn bundle;}@Overridepublic void onRestoreInstanceState(Parcelable state) {super.onRestoreInstanceState(state);// Restore View's state here}
我们平时常用的控件基本都实现了这两个方法,所以可以自动保存状态,如果我们要自定义View的话,也应该实现这两个方法用来保存状态。这里有个例外,textview需要声明android:freezeText=”true”才能保存和恢复状态。
第二,必须有Android:id属性
如果我们没有id属性的话,系统就没法找到我们的view,也就无法恢复状态。
知道了上边这一点之后,我们知道了,原来在Android的view设计中,view本身是具有状态保存和恢复功能的,所以我认为当我们在写代码的时候不应该打破这种设计模式,我们不应该在fragment的onSaveInstanceState中保存view的状态,而应该只用来保存fragment本身的状态和数据,也就是它的成员变量。这种思路是正确的。我们想一下,当我们将一个fragment加入到回退栈之后,然后在这个fragment中再打开另外一个fragment的时候,前一个fragment的视图会被销毁,但是实例还在,实例在,说明fragment的状态还在,视图销毁了,说明view的状态不在了;但是当我们从后面的fragment返回时会发现,view的状态又恢复了,我们并没有做任何额外的工作,这就说明了view是自动恢复状态的,不需要我们过多的干涉。
setRetainInstance
我们还可以通过在onCreate方法中设置setRetianInstance(true)的方法来达到保存数据的目的;当我们设置为true时,在屏幕方向改变时,fragment的实例不会被销毁,它只是被销毁了视图,并且从activity上解绑,然后重新创建的时候只会创建视图,因为实例还存在,所以不走onCreate方法。整个生命周期如下:
onDestroyView-->onCreateView-->onActivityCreated
需要特别注意的时,使用这种方法的时候,fragment不能加入到回退栈中,而且只适用于因为配置改变比如屏幕方向改变导致的fragment实例可能被销毁的状况,如果因为应用处于后台或者内存紧张等原因,fragment的实例还是可能被销毁的。具体原因可以浏览Stack Overflow
出于以上的原因,使用这种方式保存状态就有很大的局限性了。一般情况下,它可以用于保存activity的数据;比如一个activity中从网络上下载了很多数据,保留图片之类的,当屏幕方向改变时,调用onSaveInstance,保存数据到bundle中并不能有多大效果,因为bundle是一个适用于小数据的容器,如果过大可能有OOM的风险或其他问题。这个时候可以在onSaveInstance中开启一个没有视图的fragment,然后将数据保存到fragment中,然后当activity重建时,在onCreate中获得这个fragment的实例,取出数据,并且remove这个fragment,具体实现可以参考Android应用开发:Fragment与大型数据缓存
- Fragment的状态保存和恢复
- 保存和恢复Activity和Fragment状态的方法
- Activity、Fragment保存和恢复状态的最佳实现
- 保存/恢复Activity和Fragment状态的最佳实践(译)
- Activity, Fragment, WebView的状态保存和恢复
- 保存/恢复 Activity 和 Fragment 状态的最佳实
- Activity, Fragment, WebView的状态保存和恢复
- Android Fragment使用(三) Activity, Fragment, WebView的状态保存和恢复
- 我踩到的关于Fragment 状态的保存和恢复的坑
- [译]Android Activity 和 Fragment 状态保存与恢复的最佳实践
- [译]Android Activity 和 Fragment 状态保存与恢复的最佳实践
- Android Activity 和 Fragment 状态保存与恢复的最佳实践
- Android Activity 和 Fragment 状态保存与恢复的最佳实践
- Fragment状态的保存
- 保存和恢复activity的状态数据
- 保存和恢复activity的状态数据
- Activity状态的保存和恢复
- 保存和恢复activity的状态
- 微信小程序的想象力与不可想象域
- 高内聚低耦合
- Shell编程单引号,双引号和反引号的区别
- JZ2440在WIN7上无法使用DNW的替代方法
- PMP考试答题九技巧
- Fragment的状态保存和恢复
- UIImagePickerController改变statusBar颜色的问题
- java集合Collection
- Android拨号流程
- linux tar (打包.压缩.解压缩)命令说明 | tar如何解压文件到指定的目录
- evevtbus
- 一个很简单的Android手机和pc通信的例子
- Android开发环境(Windows 64bit)
- 混淆 eclipse 环境不同造成打包不成功或者部分页面闪退问题,实践解决方案