好久以前看过的一篇文章,写的很好。转载并译于:https://inthecheesefactory.co...。java
几个月之前,我发布了一篇关于Fragment状态保存和恢复的文章,那多是目前为止最好的方式用于保存/恢复 Android Fragment 的状态。我收到了不少来自世界各地的Android开发者有价值的反馈。十分感谢大家 =)android
不管如何,StatedFragment
打破了设计模式,我使用了不一样于Android状态保存/恢复的方式来设计它,这样作的目的是为了让Android开发者可以更简单的理解Fragment状态的保存与恢复,就像Activity作的同样(同时处理View状态和实例状态),因此我经过开发StatedFragment
作了一个实验,而且看看它是怎么作的,以及StatedFragment这样的设计是否更容易被理解?是否对开发者更加友好?git
如今,两个月的实验过去了,我相信我已经获得告终果。虽然StatedFragment
有点容易被理解,可是它同时带来了很大的问题。它破坏了Android View的基本框架.因此我认为这是很糟糕的,可能会致使长远的影响。事实上我已经对本身的代码感到担心了...github
因为这个缘由,我决定从如今开始弃用StatedFragment
。而且,为了弥补我错误,我写了这篇文章,用可见的方式来展现基于Android的设计如何保存和恢复Fragment的状态的最佳实践。设计模式
当Activity的onSaveInstanceState
被调用的时候,Activity将会从View 层次(View Hierachy)中的每个View中自动搜集View的状态。请注意,只会搜集实现了View状态保存/恢复的内部方法的View的数据。一旦onRestoreInstanceState被调用,Activity将会将这些搜集到的数据一对一的返还给View 层次里在搜集的时候提供了一样的android:id
属性的View。框架
让咱们看看视觉上的效果。ide
这就是为何尽管Activity已经被销毁,而咱们并无作一些特别的事情来保存状态,可是EditText中键入的文本仍然可以呈现的缘由。这并非什么魔法,这些View 的状态已经被自动的保存和恢复回来了。this
这也是为何View 在没有被设置android:id
属性的时候不能保存和恢复本身的状态的缘由。spa
尽管这些View 的状态被自动的保存了,可是Activity的成员变量并不会有一样的效果。这些成员变量会被和Activity一块儿销毁。你能够手动的保存和恢复它们,经过onSaveInstanceState
和onRestoreInstanceState
方法。设计
public class MainActivity extends AppCompatActivity { // These variable are destroyed along with Activity private int someVarA; private String someVarB; ... @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("someVarA", someVarA); outState.putString("someVarB", someVarB); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); someVarA = savedInstanceState.getInt("someVarA"); someVarB = savedInstanceState.getString("someVarB"); } }
这就是为了恢复Activity实例的状态和View 状态须要作的。
若是Fragment被系统销毁,全部事情都会发生的像Activity发生的那样。
这意味着每个单独的成员变量被销毁了。你必须分别地经过onSaveInstanceState
和onActivityCreated
方法,手动的保存和恢复这些变量。
请注意在Fragment里面没有onRestoreInstanceState
方法存在。
public class MainFragment extends Fragment { // These variable are destroyed along with Activity private int someVarA; private String someVarB; ... @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("someVarA", someVarA); outState.putString("someVarB", someVarB); } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); someVarA = savedInstanceState.getInt("someVarA"); someVarB = savedInstanceState.getString("someVarB"); } }
对于Fragment来讲,有一些特殊状况不一样于Activity,我以为你须要知道这些状况。一旦Fragment从后退栈中返回,它的View 会被销毁,并从新建立
在这种状况下,Fragment并不会被销毁,只有Fragment中的View 会被销毁。 结果是,并不会发生任何实例状态的保存。可是上面展现的这些View在Fragment生命周期中被从新建立时,发生了什么?
别惊讶,由于Android是这样设计的。在这种状况下,Fragment中的View 状态的保存/恢复会被内部调用。结果就是,每个实现了内部View 状态保存/恢复的View ,将会被自动的保存而且恢复状态,例如带有android:freezeText="true"
属性的EditText
或者TextView
。就以前完美显示的同样。
请注意,在这种状况下,只有View 被销毁(并重建)了。Fragment仍然在这里,就像它内部的成员变量同样。因此你不须要对它们作任何事情。不须要任何额外的代码。
public class MainFragment extends Fragment { // These variable still persist in this case private int someVarA; private String someVarB; ... }
你也许已经注意到了,若是Fragment中的每个View 都在内部实现了View 的状态的保存和恢复.在这种状况下,你就没有必要作任何事情,View 状态会被自动的恢复,而且Fragment中的成员变量也和以前的同样。
因此Fragment状态保存/恢复最佳实践的第一条件就是...
Android内部经过onSaveInstanceState
和 onRestoreInstanceState
方法提供了保存和恢复View 状态的机制。开发者的任务就是实现它。
public class CustomView extends View { ... @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 } ... }
基本上来讲,每个单独的Android提供的的标准View 组件都已经在内部完成了这些事情,例如EditText
,TextView
,Checkbox
.
尽管也许你手动让它生效,例如,你须要为TextView
设置android:freezeText
为true,来使用这个功能。
可是若是咱们讨论关于互联网上贡献的第三方的自定义组件。我必须说,它们大多数都没有实现这部分的代码,在使用中会致使很大的问题。
若是你决定使用第三方的自定义组件,你不得不确保它已经在内部实现了View 的保存和恢复,不然你必须建立继承这个三方组件的一个派生的子类,而且本身实现onSaveInstanceState
和onRestoreInstanceState
方法。
// // 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 } ... }
而且,若是你建立你本身的自定义View或者自定义ViewGroup,也不要忘记实现这两个方法。应用中每个类型的View实现这部分都是很重要的。
而且不要忘记为每个你须要开启View状态保存和恢复的View设置android:id
属性,否则它们的状态不能正确的恢复。
<EditText android:id="@+id/editText1" android:layout_width="match_parent" android:layout_height="wrap_content" /> <EditText android:id="@+id/editText2" android:layout_width="match_parent" android:layout_height="wrap_content" /> <CheckBox android:id="@+id/cbAgree" android:text="I agree" android:layout_width="wrap_content" android:layout_height="wrap_content" />
这篇文章已经进行到了一半!
为了使你的代码变得干净和可扩展,你最好把Fragment状态和View状态分开处理。若是这里有任何属性是属于View的,在View内部进行保存和恢复.若是这里有任何属性是属于Fragment的,在Fragment内部进行保存和恢复。这里有一个例子。
public class MainFragment extends Fragment { ... private String dataGotFromServer; @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString("dataGotFromServer", dataGotFromServer); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); dataGotFromServer = savedInstanceState.getString("dataGotFromServer"); } ... }
让我再重复一遍。不要在Fragment中的 onSaveInstanceState
中保存View状态,反之亦然。
这就是全部的内容了。这是如何保存和恢复Activity,Fragment和View状态的最佳实践。但愿你在这篇文章中找到一些有用的信息=)
请按照上面所描述的方法来保存恢复Activity,Fragment,View的状态。从如今开始,让我把StatedFragment
废弃掉吧。
还有,StatedFragment中获取onActivityResult的特性在NestedFragment中仍然是可使用的。为了不将来的混淆,我决定把这个功能分离到一个NestedActivityResultFragment
这个新的类里面,在v0.10.0以及后续的版本可用。
更多的关于它的信息参见 https://github.com/nuuneoi/St...,请随时查看。
但愿这篇文章中这种动态图片的方式可以帮助大家清楚的理解Activity,Fragment,View状态的恢复。对于前一篇文章的致使的混淆,我感到十分抱歉。^^"