The Real Best Practices to Save/Restore Activity’s and Fragment’s state
英文原文:https://inthecheesefactory.com/blog/fragment-state-saving-best-practices/en

几个月前我发布过一篇关于Fragment缓存和恢复状态的文章:Probably be the best way (?) to save/restore Android Fragment’s state so far . 收到了许多来自世界各地Android开发者的宝贵建议和反馈,在此对大家说声谢谢嗷☺。(这里的文章原文博主已经删除,国内有翻译,下文中的StatedFragment就是这篇文章中的最终产物,为的是更方便的实现Fragment状态的缓存和恢复,不过已经不推荐大家使用)。
但是StatedFragment的策略和官方设定的缓存策略是不一样的,因为官方设定的策略可能让开发者能更容易的理解Fragment的状态缓存和恢复,使其表现的像Activity那样能同时操控View和变量。为此我做了个实验来测试StatedFragment,以验证它是否更加容易理解?它的设计对开发者来说是否更加友好?经过两个月的测试,我想我已经得出结论了:虽然StatedFragment显得更容易理解,但同时它也带来了一个大问题,即它破坏了Android View的架构设计,可能在以后带来更多问题,并且,我也开始怀疑之前写的代码有点诡异… 因为这些原因,我决定从现在开始废弃掉StatedFragment。当然,为了拟补这个错误,我决定写下这篇博客来展示下Android中Fragment状态缓存和恢复的最佳实践。

理解当Activity状态被缓存或回收时发生了什么

当Activity的onSaveInstanceState 方法被调用时,Activity将自动搜集它所有子View的状态,但是要注意,只有内部实现了状态缓存和恢复接口的View才能被搜集到!(即Activity的子View必须在内部实现状态缓存和恢复的接口) ,随后在onRestoreInstanceState方法被调用的时候,Activity将根据View的id(android:id)来把缓存的数据一对一的传递回去,如下图所示。

android TabLayout设置Tab左右间距 android tablayout fragment 缓存_android


android TabLayout设置Tab左右间距 android tablayout fragment 缓存_缓存_02


正是因为这个原因,EditText才能在Activity被销毁后重建到时候仍然保持着销毁前的状态,即使我们啥也没干。这不是魔法,这些View能自动的缓存和恢复状态。这也是为什么哪些没有id(android:id)的View就不能缓存和恢复状态的原因。

虽然这些View能自动的缓存状态,但是对于Activity的成员变量来说就行不通了,成员变量会随着Activity的销毁而被回收,所以你必须通过onSaveInstanceState方法和onRestoreInstanceState方法手动地缓存和恢复它们。示例如下:

android TabLayout设置Tab左右间距 android tablayout fragment 缓存_开发者_03

理解当Fragment状态被缓存或回收时发生了什么

当Fragment被系统销毁时,它的表现和Activity是一样一样的。如下图所示:

android TabLayout设置Tab左右间距 android tablayout fragment 缓存_android_04


android TabLayout设置Tab左右间距 android tablayout fragment 缓存_状态缓存_05


这就意味着每一个成员变量也会被销毁掉,所以你必须通过onSaveInstanceState方法和onActivityCreated方法来手动地缓存和恢复状态。同志们要注意啊,Fragment里是没有onRestoreInstanceState方法的,这里和Activity不一样哟~示例如下:

android TabLayout设置Tab左右间距 android tablayout fragment 缓存_开发者_06

对于Fragment来说有些特殊情况是和Activity不一样的,我希望大家能注意下:当Fragment从栈中重新显示的时候,他的View将会被销毁重建!

android TabLayout设置Tab左右间距 android tablayout fragment 缓存_开发者_07


这种情况下要知道,Fragment对象本身并没有被销毁,只是它包含的View会销毁重建。 这种情况下是不会触发缓存状态的。那么当这些重建的View会有什么样的表现呢?其实这不是啥问题,Android早就考虑过这个。这种情况下View内部的状态缓存和恢复接口会被触发,所以只要是实现了这些接口的View都会自动缓存和恢复它自己的状态,例如具有android:freezeText=”true”属性的EditText或 TextView,这样它就和销毁前的状态一样了。如下图所示:

android TabLayout设置Tab左右间距 android tablayout fragment 缓存_android_08


再次提醒下,这种情况下只有View会销毁重建,Fragment对象本身一直没变,所以它内部的成员变量也不会被销毁,所以你不需要做额外工作。

android TabLayout设置Tab左右间距 android tablayout fragment 缓存_Android_09

呐,现在你应该明白了,只要是实现了状态缓存和恢复接口的View,在Fragment内部都能自动的进行状态缓存和恢复,而不需要添加额外的工作,那么重点来了,对于Fragment来说,缓存和恢复状态最好的办法就是~~~

应用中所有View都在其内部实现状态缓存和恢复的接口

Android已经提供了给View实现状态缓存和恢复的方法:onSaveInstanceState和 onRestoreInstanceState,这是开发者要去实现的任务。

android TabLayout设置Tab左右间距 android tablayout fragment 缓存_缓存_10


总的来说,每一个系统控件,例如EditText, TextView, Checkbox等,它们都已经在内部实现了状态缓存和恢复的接口,在使用这些特性的时候只需要设置一下相关的属性为true即可,例如TextView 设置android:freezeText=”true”。

但是,网上众多第三方控件呢,老实讲,很多都没有在内部实现这些接口,导致在实际使用过程中可能带来大麻烦。

如果你决定要使用第三方控件,那么你必须要确认它有在内部实现缓存和恢复状态的接口,否则你需要继承它去写一个子控件来手动实现 onSaveInstanceState/onRestoreInstanceState .示例如下

android TabLayout设置Tab左右间距 android tablayout fragment 缓存_开发者_11


此外,别忘了当你直接继承自View或ViewGroup的时候也要去实现这两个方法,这真的真的真的很重要哟~

还有!别忘了给那些你希望能自动缓存和恢复状态的View添加id!!!

android TabLayout设置Tab左右间距 android tablayout fragment 缓存_状态缓存_12


到此,我们已经搞定一半的工作了!!!明确的区分开Fragment的状态和View的状态

为了使你的代码更简洁和更具维护性,你必须区分开Fragment的状态和View的状态。如果一个属性是属于View的,那么在View内部去缓存和恢复它,如果一个属性是属于Fragment的,那么在Fragment内部是缓存和恢复它。(罗里吧嗦的,反正就是不要混淆了View和Fragment的状态嘛) ,示例如下:

android TabLayout设置Tab左右间距 android tablayout fragment 缓存_android_13


本帅再次提醒:不要在Fragment的onSaveInstanceState方法里缓存View的状态,反之亦然。

好了,这就是Activity/Fragment 状态缓存和恢复的最佳实践,希望能对你有帮助。