为什么要使用Fragment#setRetainInstance(boolean)?

72
我觉得Fragment#setRetainInstance(true)很令人困惑。以下是从Android开发者API中提取的Javadoc:

public void setRetainInstance (boolean retain)

控制一个片段实例是否在Activity重新创建(例如通过配置更改)时保留。这只适用于不在回退栈中的碎片。如果设置,当活动重新创建时,片段生命周期将略有不同:

  • onDestroy()将不会被调用(但onDetach()仍将被调用,因为片段正在从其当前活动中分离出来)。
  • onCreate(Bundle)将不会被调用,因为片段不会被重新创建。
  • onAttach(Activity)和onActivityCreated(Bundle)仍将被调用。
问题:作为开发人员,您如何使用它,并且为什么它使事情变得更容易? 作为开发人员,您可以使用Fragment#setRetainInstance(true)方法来保留Fragment的实例,即使它们所关联的Activity被销毁和重新创建。这意味着您可以保持Fragment的状态和成员变量在Activity重新创建后不变。
使用这个方法可以使开发更容易,因为它允许您在Activity重新创建后仍然保持Fragment的状态和成员变量。例如,如果您有一个需要进行网络请求的Fragment,您可能不想在每次Activity被销毁和重新创建时都重新执行请求,而是希望保留请求结果并在Activity重新创建后恢复它们。通过使用setRetainInstance(true),您可以轻松地实现这一点,而无需在Activity重新创建时手动保存和恢复Fragment的状态。

3
类似的问题,有很好的信息:理解Fragment的setRetainInstance(boolean)方法 - Richard Le Mesurier
1
如果设备内存不足,将调用onDestroy() - James Goodwin
4个回答

93

开发者如何使用这个功能?

调用setRetainInstance(true)方法。我通常在onCreateView()onActivityCreated()中使用它。

为什么使用这个功能会更容易?

相比于使用onRetainNonConfigurationInstance()来处理数据在配置更改(例如从竖屏到横屏)时的保留,setRetainInstance(true)更加简单。未被保留的Fragment会在配置更改时被销毁和重新创建,而被保留的Fragment则不会。因此,任何被保留Fragment所持有的数据仍然可以在配置更改后的Activity中使用。


4
请检查该片段是否存在(例如,使用 findFragmentById())。如果存在,请不要重新创建它。 - CommonsWare
1
@androiddeveloper:“如果片段保存其视图,以便不必在onCreate()方法中重新创建它们,则可能会占用一些内存”--您可以尝试将小部件重新父级到新活动中,但我没有成功过。据我所知,您需要在onCreateView()中重新创建小部件。“但是,我认为使用softReference/weakReference来完成这个任务也可以,对吗?”--什么任务? - CommonsWare
3
应该可以。我很少在片段中实现onCreate(),所以我没有尝试过。 - CommonsWare
1
@e.shishkin 你可以随时调用它。从技术上讲,你甚至不需要在生命周期方法中调用它(虽然这通常不是你想要或需要做的事情)。 - Alex Lockwood
如果用户需要深入到层次结构中,那么这是否适用于ListFragment,然后使用"getRetainInstance()"来保留实例,以便在用户点击返回按钮时使用?还是一般的做法是在双面板应用程序中使用"addToBackStack"来重新创建过去的ListFragment? - whyoz
显示剩余11条评论

49

将长时间运行的资源保持打开状态非常有用,例如套接字。建立一个没有UI的片段来保存对蓝牙套接字的引用,这样当用户翻转手机时就不必担心重新连接它们。

在保留片段中保存加载时间长的资源的引用也很方便,如位图或服务器数据。只需加载一次并将其保存在保留片段中,当活动重新加载时它仍然存在,您无需重新构建它。


不错。但是如果我们需要刷新它们怎么办? - Anshul Tyagi
我认为这是单例模式通常要做的事情,我的想法有误吗? - rupinderjeet
@rupinderjeet 是的。单例模式是另一种实现它的方式。单例模式被认为是“反模式”,有些人不愿使用它们。片段还具有与活动生命周期相关联的优点,因此您可以获得生命周期事件。例如,如果用户离开应用程序,您可以销毁资源(这也可以使用单例模式完成,但需要更多的代码)。 - DeeV

35

很晚才回答,但我觉得这会让问题更清楚明白。跟我说:

FALSE

  • 当配置更改时,片段将被重新创建。创建一个新实例。
  • 所有生命周期方法都会在配置更改时调用,包括onCreate()onDestroy()

TRUE

  • 片段不会在配置更改时重新创建。使用相同的实例。
  • 除了onCreate()onDestroy(),所有生命周期方法都会在配置更改时调用。
  • 当添加到后退栈时,保留实例将不起作用。

请记住,以上内容同样适用于DialogFragment以及Fragments。


1
你能否在文本中添加“当添加到后退栈时,保留实例将不起作用”的引用? - nmxprime
1
@nmxprime 在这里:http://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean) - android developer
1
如果片段被添加到后退栈中,我该如何恢复其状态?savedInstanceState为空,而且我不能使用setRetainInstance... - android developer

5

setRetainInstance(boolean)方法已被弃用,请使用ViewModels。

自从Fragment API版本1.3.0起,Fragment的setRetainInstance(boolean)方法已被弃用

随着引入ViewModels,开发者有了可以与活动、片段和导航图相关联以保留状态的特定API。这使得开发者可以使用一个普通的非保留Fragment,并将他们想要保留的特定状态分离出来。

这确保了开发者对于这些Fragment拥有更加易懂的生命周期(与其它Fragment相匹配),同时保持单一创建和销毁的有用属性(在这种情况下,ViewModel的构造函数和ViewModel的onCleared()回调)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接