理解 Fragment 的 setRetainInstance(boolean)

Posted

技术标签:

【中文标题】理解 Fragment 的 setRetainInstance(boolean)【英文标题】:Understanding Fragment's setRetainInstance(boolean) 【发布时间】:2012-06-26 06:52:06 【问题描述】:

从文档开始:

public void setRetainInstance(布尔保留)

控制是否在 Activity 重新创建(例如配置更改)期间保留片段实例。这只能与不在后台堆栈中的片段一起使用。如果设置,在重新创建活动时片段生命周期将略有不同:

onDestroy() 将不会被调用(但 onDetach() 仍会被调用,因为片段正在与其当前活动分离)。 onCreate(Bundle) 不会被调用,因为片段没有被重新创建。 onAttach(Activity) 和 onActivityCreated(Bundle) 仍将被调用。

我有一些问题:

片段是否也保留其视图,还是会在配置更改时重新创建? “保留”到底是什么意思?

当用户离开activity时,fragment会被销毁吗?

为什么它不适用于后堆栈上的片段?

在哪些用例中使用此方法有意义?

【问题讨论】:

类似的问题有很好的信息:Why use Fragment#setRetainInstance(boolean)? 多窗口(API 24)?你在这里读到的任何东西都是不可靠的。我看到Fragment.onDestroy() intermittently 调用了相应的片段中的字段无效,据称受setRetainInstance(true) 保护 【参考方案1】:

首先,查看my post 保留的片段。这可能会有所帮助。

现在回答你的问题:

片段是否也保留其 view 状态,还是会在配置更改时重新创建 - 究竟什么是“保留”?

是的,Fragment 的状态将在配置更改期间保留。具体来说,“保留”意味着片段将在配置更改时被销毁。也就是说,Fragment 将被保留,即使配置更改导致底层 Activity 被破坏。

当用户离开activity时,fragment会被销毁吗?

就像Activitys 一样,Fragments 可能会在内存资源不足时被系统销毁。无论您是否让您的片段在配置更改期间保留其实例状态,都不会影响您离开Activity 后系统是否会销毁Fragments。如果您离开Activity(即按下主页按钮),Fragments 可能会或可能不会被销毁。如果您通过按后退按钮离开Activity(因此,调用finish() 并有效地销毁Activity),所有附加Fragments 的Activitys 也将被销毁。

为什么它不适用于后堆栈上的片段?

不支持它的原因可能有多种,但对我来说最明显的原因是Activity 持有对FragmentManager 的引用,而FragmentManager 管理后台堆栈。也就是说,无论您是否选择保留 Fragments,Activity(以及 FragmentManager 的后台堆栈)都将在配置更改时被销毁。它可能不起作用的另一个原因是,如果允许保留片段 非保留片段都存在于同一个 backstack 上,事情可能会变得很棘手。

在哪些用例中使用此方法有意义?

保留的片段对于跨活动实例传播状态信息(尤其是线程管理)非常有用。例如,片段可以作为ThreadAsyncTask 实例的宿主,管理其操作。有关此主题的更多信息,请参阅 my blog post

一般来说,我会像使用onConfigurationChangedActivity 一样对待它...不要仅仅因为您太懒而无法正确实施/处理方向更改而将其用作创可贴。仅在需要时使用它。

【讨论】:

视图对象不会被保留,它们总是在配置更改时被销毁。 据我所知,如果你有setRetainInstance(true)Fragment java 对象,它的所有内容不会在旋转时被破坏,但是视图被重新创建。即再次调用onCreatedView()。这基本上是自 android 1.0 以来它应该与 Activities 一起工作的方式。我不认为使用它是“懒惰的”,或者使用它是不“正确的”。事实上,我不明白为什么它不是默认设置,或者你为什么想要关闭它。 我找到了您对“为什么它不适用于后堆栈上的片段?”的解释。难以理解。但也许我很笨:( @dierre 可以通过多种方式销毁活动。例如,如果单击“返回”,则该活动将被销毁。如果您单击“主页”,则活动将停止,并且在将来的某个时间内存不足时可能会被破坏。保留Fragments 仅在配置更改时保留,其中底层活动将被销毁并立即重新创建。在活动被销毁的所有其他情况下,保留的片段也将被销毁。 @AlexLockwood 您能否确认以下内容:即使使用了setRetainInstance(true),仍然必须实现自己的持久性(savedInstanceState 或其他)才能能够处理所有场景:例如“home key, rotate, back to app”用构造函数调用重新创建我的片段,丢失所有状态变量。我有一个AsyncTask 作为成员变量,这就是我想要保留的原因,现在,如果我想让它工作,我不得不停止任务、保存状态并在用户回来时恢复。所以总而言之,这只是一种帮助旋转的快速方法,但一般来说没有用。【参考方案2】:

SetRetainInstance(true) 允许片段生存。其成员将在轮换等配置更改期间保留。但是当活动在后台被杀死时,它仍然可能被杀死。如果后台的包含活动被系统杀死,它的 instanceState 应该由您正确处理 onSaveInstanceState 的系统保存。换句话说,onSaveInstanceState 将始终被调用。虽然如果 SetRetainInstance 为 true 并且片段/活动尚未被杀死,则不会调用 onCreateView,但如果它被杀死并被尝试恢复,它仍然会被调用。

以下是对 android 活动/片段的一些分析,希望对您有所帮助。 http://ideaventure.blogspot.com.au/2014/01/android-activityfragment-life-cycle.html

【讨论】:

我肯定看到在旋转屏幕时在保留的片段上再次调用 onCreateView。 这个链接是你自己的博客吗?如果是这种情况,你应该说清楚。【参考方案3】:

setRetaininstance 仅在您的 activity 因配置更改而被销毁并重新创建时有用,因为实例是在调用 onRetainNonConfigurationInstance 期间保存的。也就是说,如果您旋转设备,保留的片段将保留在那里(它们不会被销毁和重新创建。)但是当运行时终止活动以回收资源时,什么都不会留下。当你按下返回按钮并退出活动时,一切都被破坏了。

通常我用这个功能来保存方向改变时间。假设我从服务器下载了一堆位图,每个1MB,当用户不小心旋转他的设备时,我当然不想做所有的下载工作再次。所以我创建了一个Fragment 保存我的位图并将其添加到管理器并调用setRetainInstance,即使屏幕方向发生变化,所有位图仍然存在。

【讨论】:

您是否创建“仅数据”片段(没有任何小部件)只是作为位图的持有者,还是这些片段也可以有小部件?当片段包含与上下文/活动相关的内容时,我已经阅读了一些关于产生内存泄漏的危险...... 框架会为你清除mActivity引用。但我不知道在这种情况下运行时是否也会清除片段实例中的小部件。请尝试一下或深入研究源代码。 我们何时可以使用 setRetaininstance 的好例子【参考方案4】:

setRetainInstance(boolean) 在你想要一些不依赖于 Activity 生命周期的组件时很有用。例如rxloader 使用这种技术来“处理 rxjava 的 Observable 的 Android 活动生命周期”(我发现了 here)。

【讨论】:

【参考方案5】:

setRetainInstance() - 已弃用

作为片段Version 1.3.0-alpha01

片段上的 setRetainInstance() 方法已被弃用。和 ViewModels 的引入,开发者有一个特定的 API 用于 保留可以与活动、片段和 导航图。这允许开发人员使用普通的,而不是 保留 Fragment 并保持他们想要保留的特定状态 分开,避免常见的泄漏源,同时保持 单个创建和销毁保留的有用属性 state(即 ViewModel 的构造函数和 onCleared() 它收到的回调)。

【讨论】:

以上是关于理解 Fragment 的 setRetainInstance(boolean)的主要内容,如果未能解决你的问题,请参考以下文章

理解Fragment生命周期

教你理解Fragment

理解 Android Fragment

理解 Fragment 的 setRetainInstance(boolean)

Fragment

安卓第十天笔记-fragment