Android中保存和恢复Fragment状态的最好方法

英文原文:Probably be the best way (?) to save/restore Android Fragment’s state so far

关键点:Fragment的Arguments。

经过这几年使用Fragment之后,我想说,Fragment的确是一种充满智慧的设计,但是使用Fragment时有太多需要我们逐一解决的问题,尤其是在处理数据保持的时候。

首先,虽然其有类似于activity的onSaveInstanceState,但是别想仅仅靠onSaveInstanceState就能保持数据。

下面就是一些案例:

情景一:stack中只有一个Fragment的时候旋转屏幕

是的,旋转屏幕是测试数据保持最简单的方法。这种情况非常简单,你只需在onSaveInstanceState存储会在旋转的时候会丢失的数据,包括变量,然后在onActivityCreated或者onViewStateRestored中取出来:

int someVar;@Overrideprotected void onSaveInstanceState(Bundle outState) { outState.putInt("someVar", someVar); outState.putString(“text”, tv1.getText().toString());}@Overridepublic void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); someVar = savedInstanceState.getInt("someVar", 0); tv1.setText(savedInstanceState.getString(“text”));}

看起来很简单是吧,但是存在这样的情况,View重建,但是onSaveInstanceState未被调用,这意味着UI上的所有东西都丢失了,请看下面的案例。

情景2:Fragment从回退栈的返回

当fragment从backstack中返回(这里是Fragment A),根据 官方文档 对Fragment生命周期的描述,Fragment A中的view会重建。

从这张图可以看到,当Fragment从回退栈中返回的时候,onDestroyView 和 onCreateView被调用,但是onSaveInstanceState貌似没有被调用,这就导致了一切UI数据都回到了xml布局中定义的初始状态。当然,那些内部实现了状态保存的view,比如有android:freezeText属性的EditText和TextView,仍然可以保持其状态,因为Fragment可以为他们保持数据,但是开发者没法获得这些事件,我们只能手动的在onDestroyView中保存这些数据。

大概流程如下:

@Overridepublic void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); // 这里保存数据}@Overridepublic void onDestroyView() { super.onDestroyView(); // 如果onSaveInstanceState没被调用,这里也可以保存数据}

但是问题来了onSaveInstanceState中保存数据很简单,因为它有Bundle参数,但是onDestroyView没有,那保存在哪里呢?答案是能和Fragment一起共存的Argument。

代码大致如下:

Bundle savedState;@Overridepublic void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // Restore State Here if (!restoreStateFromArguments()) {// First Time running, Initialize something here }}@Overridepublic void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); // Save State Here saveStateToArguments();}@Overridepublic void onDestroyView() { super.onDestroyView(); // Save State Here saveStateToArguments();}private void saveStateToArguments() { savedState = saveState(); if (savedState != null) {Bundle b = getArguments();b.putBundle(“internalSavedViewState8954201239547”, savedState); }}private boolean restoreStateFromArguments() { Bundle b = getArguments(); savedState = b.getBundle(“internalSavedViewState8954201239547”); if (savedState != null) {restoreState();return true; } return false;}/////////////////////////////////// 取出状态数据/////////////////////////////////private void restoreState() { if (savedState != null) {//比如//tv1.setText(savedState.getString(“text”)); }}//////////////////////////////// 保存状态数据//////////////////////////////private Bundle saveState() { Bundle state = new Bundle(); // 比如 //state.putString(“text”, tv1.getText().toString()); return state;}

现在你可以轻松的在fragment的saveState和restoreState中分别存储和取出数据了。现在看起来好多了,几乎快要成功了,但是还有更极端的情况。

情景3:在回退栈中有一个以上的Fragment的时候旋转两次

当你旋转一次屏幕,onSaveInstanceState被调用,UI的状态会如预期的那样被保存,,但是当你再一次旋转屏幕,上面的代码就可能会崩溃。原因是虽然onSaveInstanceState被调用了,但是当你旋转屏幕,回退栈中Fragment的view将会销毁,同时在返回之前不会重建。这就导致了当你再一次旋转屏幕,没有可以保存数据的view。saveState()将会引用到一个不存在的view而导致空指针异常NullPointerException,因此需要先检查view是否存在。如果存在保存其状态数据,将Argument中的数据再次保存一遍,或者干脆啥也不做,因为第一次已经保存了。

private void saveStateToArguments() { if (getView() != null)savedState = saveState(); if (savedState != null) {Bundle b = getArguments();b.putBundle(“savedState”, savedState); }}

Fragment的最终模版:你被雨淋湿的心,是否依旧。

Android中保存和恢复Fragment状态的最好方法

相关文章:

你感兴趣的文章:

标签云: