当片段被替换并放回堆栈(或取出)时,它是否保留在内存中?


72

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

活性的启动活动B,而是在屏幕上,该系统能够以除去从存储器如果它是由系统所需的。按BACK后,将重新创建A到内存中,就好像它从未放在第一位一样。

我一直在寻找对Fragments在内存方面会发生什么的清晰解释,但没有发现任何东西。是否以相同的方式工作?例如:

活动C在其布局中具有片段F。然后,在某个点FFragment G替换,但是F保留在其后堆栈中。

威尔˚F留在内存中,直到ç被杀害或者可以在由系统根据需要被删除?

我真正要问的是,如果我在一个Activity中有复杂的片段后退堆栈,是否会出现内存不足的风险?


6
请注意,您的第一个假设是错误的:活动“ A”将永远不会从内存中删除。参见commonsware.com/blog/2011/10/03/…–
Thierry-Dimitri Roy

1
@ Thierry-DimitriRoy,令人难以置信...我觉得我一直在撒谎...我几乎不相信它...
cottonBallPaws 2012年

系统不会杀死单个活动或片段。当内存不足时,它只能杀死整个过程来回收内存。
stdout

Answers:


103

看一下这个:BackStackRecord.Op.fragment

这就是将碎片存储在后堆栈中的方式。请注意实时参考,此处也WeakReferenceSoftReference使用。

现在这个:FragmentManagerImpl.mBackStack

那是经理存储后堆栈的地方。也很简单ArrayList,没有WR或SR。

最后是:Activity.mFragments

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

GC只能收集没有实时引用(无法从任何线程访问)的对象。这意味着,直到您的Activity被销毁(因此,FragmentManager引用消失了),GC才能收集回栈中的任何碎片

请注意,当Activity被销毁并保留状态时(例如,将设备转为横向模式时),它不会保留Fragment堆栈中的实际对象,而仅保留其状态-Fragment.FragmentState对象,即重新堆栈中的实际片段-每次在保留状态下重新创建活动时创建。

希望这可以帮助。

PS因此,简而言之:是的,您可以通过添加Fragments回堆栈以及添加太多视图以查看层次结构来耗尽内存

UPD考虑到您的示例,F将保留在内存中,直到C被杀死为止。如果C被杀死,然后以不同的配置复活-F也将被销毁并转生到另一个物体中。因此,F的内存占用量一直存在,直到C失去状态或清除后栈为止。


30
但是请注意,将片段放在后堆栈上时,将调用其onDestroyView()。如果那时您清理了该片段所持有的所有主要分配,那么您就不会有内存问题。
hackbod 2012年

1
是的,很明确。片段对象仍然会占用一定数量的内存,严格来说,即使清理逻辑到位,一个对象仍然可以用大量的片段淹没内存,但是您绝对是正确的-如果在实际情况下正确清理,(例如,当碎片数量合理时),后向堆栈碎片不应造成任何可观的内存开销。
伊万·巴佐夫

1
@hackbod是什么意思major allocations
theblang 2014年

@mattblang如果可以的话,Dianne最有可能意味着任何占用大量内存的内容,而不是由fragment的视图层次结构管理的,最著名的例子是Bitmaps。如果您的片段保留了几个基元或平均大小的Strings-可能就可以了。但是处理掉所有可能使您超出进程内存上限的实例。
伊万·巴佐夫

3
@traninho好主意。当放回堆栈时,Fragment实例不会被存储,因此onDestroyView()应始终将其拥有的所有视图引用都为空。否则,它将阻止无效视图被GC。另外,setRetainInstance(true)即使Activity重新创建,非后退保留的片段(请参见)也不会被破坏,因此,通过保留视图引用,您可能会泄漏整个对象Activity。通常情况下不会发生这种情况,因为在99%的情况下,视图引用onCreateView()都会在Actvity休闲娱乐中得到更新,因此在此之后,引用仍然指向新视图。
伊万·巴佐夫

7

很抱歉无法为您提供一些正式的信息来源,但是我也很好奇看到会发生什么并决定对其进行测试。根据我的测试,是的,您将面临内存不足的风险。

我必须在for循环中添加数量惊人的Fragments(超过一百个)OutOfMemoryError才能实现,但是确实发生了。和检查我的日志,我可以看到onCreate()onCreateView()方法,被称为很多次了,但是onSaveInstance()onPause()onDestroy所有不叫。

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

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

我添加的片段有些简单:一个ImageView,一个EditText,一对TextViews,一个SeekBar和一个ListView

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

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


4

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

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


谢谢比尔。但是,仍然没有说是将活动的后堆栈中的Fragments保留在内存中还是根据需要进行释放,这就是我想知道的……
cottonBallPaws

@littleFluffyKitty为什么这不能回答您的问题?显然,它们在内存中,但是像常规活动一样暂停,直到您返回或销毁活动后,它们才会被清除。
Warpzit 2012年

1

是的,您可能会用光内存,从而在单个活动中创建太多碎片。仅当包含Activity时,片段才会被销毁。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.