当 Fragment 被替换并放入回栈(或删除)时,它是不是保留在内存中?

Posted

技术标签:

【中文标题】当 Fragment 被替换并放入回栈(或删除)时,它是不是保留在内存中?【英文标题】:When a Fragment is replaced and put in the back stack (or removed) does it stay in memory?当 Fragment 被替换并放入回栈(或删除)时,它是否保留在内存中? 【发布时间】:2012-01-18 22:28:18 【问题描述】:

行为是否类似于活动的工作方式?例如,对于活动,它的工作方式如下:

Activity A启动Activity B,当B在屏幕上时,系统可以移除A如果系统需要,则从内存中获取。按 BACK 后,A 将被重新创建到内存中,就好像它从未离开过一样。

我一直在寻找一个清晰的解释,说明片段在记忆方面会发生什么,但没有找到任何东西。它的工作方式相同吗?例如:

Activity C 在其布局中有Fragment F。然后,在某些时候 FFragment G 替换,但 F 保留在其后堆栈中。

F 会一直保留在内存中直到 C 被杀死,还是系统可以根据需要将其删除?

我真正要问的是,如果我在单个 Activity 中有一堆复杂的 Fragment,我是否会冒内存不足的风险?

【问题讨论】:

请注意,您的第一个假设是不正确的:活动“A”永远不会从内存中删除。见commonsware.com/blog/2011/10/03/… @Thierry-DimitriRoy,难以置信...我觉得我一直生活在一个谎言中...我几乎不相信... 系统不会杀死单个活动或片段。它只能在内存不足时杀死整个进程以回收内存。 【参考方案1】:

看看这个:BackStackRecord.Op.fragment

这就是片段存储在后台堆栈中的方式。注意实时参考,WeakReferenceSoftReference 都没有使用。

现在这个:FragmentManagerImpl.mBackStack

这是管理器存储后台堆栈的地方。简单的ArrayList,也没有 WR 或 SR。

最后是这个:Activity.mFragments

这是对片段管理器的引用。

GC 只能收集没有实时引用的对象(不能从任何线程访问)。这意味着,直到您的 Activity 被销毁(因此,FragmentManager 引用已消失),GC 将无法收集后台堆栈中的任何 Fragment

请注意,当 Activity 被销毁并且retains state(例如当您将设备切换到横向模式时)时,它不会在堆栈中保留实际的 Fragment 对象,仅保留它们的状态 - Fragment.FragmentState 对象,即实际每次使用保留状态重新创建活动时,都会重新创建后台堆栈中的片段。

希望这会有所帮助。

PS 所以,简而言之:是的,您可以通过将 Fragments 添加到后台堆栈以及向视图层次结构添加太多视图来耗尽内存.

UPD 考虑到您的示例,F 将保留在内存中,直到 C 被杀死。如果 C 被杀死然后以不同的配置复活 - F 也将被破坏并在不同的对象中重生。因此,在 C 丢失状态或清除返回堆栈之前,F 的内存占用量一直存在。

【讨论】:

但是请注意,当一个片段被放入后栈时,它的 onDestroyView() 会被调用。如果此时您清理了片段所持有的任何主要分配,那么您应该不会遇到内存问题。 是的,实心点。片段对象仍然会占用一定数量的内存,严格来说,即使清理逻辑到位,一个仍然可以用大量片段淹没内存,但你绝对是对的 - 如果清理得当,在实际条件下(即,当碎片数量合理时)回栈碎片不应导致任何可观的内存开销。 @hackbod major allocations 是什么意思? @mattblang 如果可以的话,Dianne 很可能是指任何内存密集且不受片段视图层次结构管理的东西,最值得注意的例子是Bitmaps。如果您的片段包含几个基元或平均大小的Strings - 那可能没问题。但是请处理任何几个实例会使您超过进程内存上限的任何东西。 @traninho 好主意。当放回堆栈时,Fragment 实例不会被销毁,因此onDestroyView() 应该始终为空它拥有的任何视图引用。否则它将阻止死视图获得 GCed。此外,即使重新创建Activity,非回栈保留片段(参见setRetainInstance(true))也不会被破坏,因此通过保留视图引用,您可以泄漏整个Activity。但通常不会发生这种情况,因为在 99% 的情况下,视图引用会在 onCreateView() 中更新,这确实会在 Actvity 游戏中被调用,因此无论如何,引用都会指向新视图。【参考方案2】:

是的,您可能会耗尽内存,在单个活动中创建太多片段。只有当包含的 Activity 存在时,碎片才会被销毁。

【讨论】:

【参考方案3】:

很抱歉无法为您提供一些官方信息来源,但我也很好奇会发生什么,并决定对其进行测试。根据我的测试,是的,你会冒着内存不足的风险。

我必须在 for 循环中添加数量惊人的 Fragment(超过一百个)才能使 OutOfMemoryError 发生,但它确实发生了。检查我的日志,我可以看到 onCreate()onCreateView() 方法被调用了很多次,但 onSaveInstance()onPause()onDestroy 根本没有被调用。

作为参考,这是我将片段添加到后台堆栈的方式:

getSupportFragmentManager().beginTransaction().add(R.id.scene_fragment_container, mSceneFragment).addToBackStack("FOOBAR").commit();

我添加的片段有些简单:ImageViewEditText、一对TextViewsSeekBarListView

但除非您在内存中保存大量数据,否则这应该不是问题。

后来我尝试只向后台堆栈添加 50 个,杀死应用程序并重新启动它。正如我希望/猜测的那样,所有片段都已恢复(并调用了 onSaveInstance()onPause() 方法),因此我的生命周期实现不是导致 OutOfMemoryError 触发的问题。

【讨论】:

【参考方案4】:

来自 developer.android.com/guide/topics/fundamentals/fragments.html

片段必须始终嵌入到活动中,并且片段的生命周期直接受宿主活动生命周期的影响。例如,当activity暂停时,它里面的所有fragment都会暂停,当activity被销毁时,所有fragment也会暂停。然而,当一个活动正在运行时(它处于恢复的生命周期状态),您可以独立地操作每个片段,例如添加或删除它们。当您执行这样的片段事务时,您还可以将其添加到由 Activity 管理的回退堆栈中 - 活动中的每个回退堆栈条目都是发生的片段事务的记录。后退堆栈允许用户通过按 BACK 按钮来反转片段事务(向后导航)。

【讨论】:

谢谢比尔。但是,这仍然不能以一种或另一种方式说明活动后堆栈中的片段是保留在内存中还是根据需要释放,这就是我想知道的...... @littleFluffyKitty 为什么不回答你的问题?显然它们在内存中,但就像常规活动一样暂停,直到您返回或您的活动被破坏时才会清理它们。

以上是关于当 Fragment 被替换并放入回栈(或删除)时,它是不是保留在内存中?的主要内容,如果未能解决你的问题,请参考以下文章

使用片段清除回栈

Android Fragment 回栈的问题

回栈事务后如何持久化分片数据?

如何使用事务回栈来弹出片段?

DialogFragment dismiss() 不弹出回栈

在 Popbackstack() 上调用方法