带有setRetainInstance(true)的Fragment在进程关闭后是否能够保留?

13
考虑到这种情况:如果我创建了一个活动并将其移至后台,而该活动包含一个设置为 setRetainInstance(true)Fragment,那么 Android 操作系统有可能在某个时刻仍然会决定关闭活动的主进程以释放内存。

然后,Activity 的状态通过 onSaveInstanceState(Bundle) 保存,相关的 Bundle 被写入文件系统以便在进程关闭后保留状态(因此要求包中的对象是可序列化的)。稍后,应用程序状态可以通过 onRestoreInstanceState(Bundle) 在新进程中恢复。

相比之下,我的 Fragment 可以包含不一定是可序列化的变量。因此,我想,Fragment 不能像 Bundle 一样存储在磁盘上。那么当进程被杀死时,我的 fragment 会发生什么呢?

我在阅读开发人员指南(http://developer.android.com/guide/components/processes-and-threads.html)时对此感到好奇:

 

持有一个当前没有显示给用户的活动的进程   (已调用活动的 onStop() 方法)。这些进程对用户体验没有直接影响,   而且系统可以随时杀死它们以回收内存   用于前台、可见或服务进程。通常有许多后台进程正在运行,因此它们   保存在 LRU(最近最少使用)列表中,以确保   最后一个被杀死的是最近被用户看到的活动所在的进程。如果活动实现其生命周期方法   正确,并保存其当前状态,则杀死其进程不会   对用户体验产生可见效果,因为当用户   返回到该活动时,活动将恢复所有可见状态。

我理解以上的killing是指VM实例被关闭并将进程状态写入文件系统(此时需要用到 Bundle)。稍后,会读取bundles以恢复进程。由于保留片段与生命周期方法无关,并且我不知道如何保留例如网络连接指针之类的内容(无论如何,您永远不应该在片段中有这样的指针),因此我想知道如果进程在此期间关闭,是否仍然会恢复片段。 我得出结论,它们肯定需要重新创建,并且尽可能使用生命周期方法而不是setRetainInstance(true)

这种假设有意义吗?

1个回答

21
看起来你在混淆两个概念。
跨配置更改保存状态不涉及序列化。如果您为Fragment请求setRetainInstance(),那么这意味着它将完全留在内存中,而不仅仅是为了配置更改而重新创建。类似的机制也适用于Activity对象,但它们需要显式定义一个要保存的Object。这通过Activity.onRetainNonConfigurationInstance()实现,而不是通过onSaveInstanceState()
另一种机制涉及序列化和可能(可能不总是,不确定)需要文件系统I / O才能重现状态信息,即使销毁了Activity / Fragment(独立于其托管的进程)。这通过Activity.onSaveInstanceState()Fragment.onSaveInstanceState()实现。
当然,您可以使用第二种机制来实现第一种目的,从而减慢应用程序处理配置更改的速度。根据您的内部状态,减速可能会很小或很大。
关于您的问题。
“相比之下,我的片段允许包含不可序列化的变量。”嗯,对于您的Activity也是如此。它可以包含无法序列化的对象,如上所述,可以在配置更改期间保存。
“当进程关闭并删除内存中的活动时,片段无法存储到磁盘并且必须在还原活动时重新创建。”不,这两种机制都适用于两种对象类型。
希望我能为澄清一些内容做出贡献。
在您的第一条评论之后进行了编辑。
关于评论:
"onRetainNonConfigurationInstance已过时":是的。我提到它是为了演示目的,因为您的问题中有一些特定的措辞。此外,根据今天(官方谷歌数字)的Android 2设备占有46%的市场份额,该方法肯定会存在很长时间,无论是否弃用。
"我的主要担忧是当我的托管进程被杀死并从内存中删除时,片段实例将发生什么情况":您的片段实例将从内存中删除,当然没有办法自动地以其完整的内部状态自动恢复。只有在需要处理配置更改的情况下才会这样做,即使用setRetainInstanceState。(但请注意,这涉及实例,换句话说,完整的对象。)
关于您的编辑:
  • 再次强调,,无论你是否使用setRetainInstanceState,如果你使用Fragment.onSaveInstanceState()来保存数据,那么你的FragmentBundle将被存储并恢复到/从Bundle中,对于所有有意义的内容。
  • 并不是"它的所有可见状态"都会被保存,正如你所引用的文本所声称的那样;例如,visibility属性将不会被保存。我不知道这是个bug还是一个feature,但这是一个事实。但这只是一个旁观者;UI元素将保存它们大部分相关的状态。
  • "进程状态被写入文件系统": 不!能够将其状态保存到Bundle的对象,实际上实现了保存它们的状态的对象将被保存在Bundle中,这意味着如果你想要你的Fragment保存一些状态信息,那么你必须自己提供这些信息。同样,再次强调:不仅与杀掉进程有关,而且与删除不可见的ActivityFragment对象有关,例如最后显示的Activity--Process可能仍然活着。
  • "bundles are read for resuming the process": 不,将读取Bundle传递给重建Activity和/或Fragment对象的过程,在这个过程中没有自动完成任何操作(除了保存其状态的库对象还原它们的状态),但是Android不会从这些Bundle中"恢复"进程。
  • "由于保留片段与生命周期方法无关": 再次强调,我认为你混淆了两个概念。只有在配置更改时,如果你通过setRetainInstance请求,才会执行Fragment的"保留"操作,但是我们大多数情况下谈论的是从Bundle重新创建Fragment对象,在这里涉及到Google文档记录的生命周期方法。
  • "我不知道如何保留例如对网络连接的指针": 再次强调,这必须是基于你的混淆的声明。当然,你可以在配置更改时保留对网络连接的引用(如setRetainInstance所请求的那样),因为当发生这种情况时,一切都简单地保留在内存中。此外,即使你的Fragment被删除(因为它变得不可见),你的进程仍然存在(因为它显示下一个Activity),你也可以(而且应该)在你的Application对象中保留对昂贵对象的引用,例如网络连接,在你的进程存在的时间内(或多或少)。只有当Android杀掉你整个app时,你才会失去一切,但我们正在讨论的序列化发生得更加频繁。

你的结论:

我的结论是,他们肯定需要被重新创建,因此在可能的情况下,生命周期方法比setRetainInstance(true)更可取。这个假设有任何意义吗?

很不幸,不太对,因为你混淆了完全独立的概念。

我最后再试一次:

  • 您将想要在您的Application对象中保留一个网络连接引用,因为如果您在应用程序中经常从头开始创建它,那么用户体验会很差。
  • 只有当Android终止您的应用程序时,您的Application对象才会死亡。
  • 当用户在您的应用程序中向前移动时,您的ActivityFragment对象将定期从您的应用程序中删除。
  • 当用户按“返回”按钮时,Android将使用生命周期方法从Bundle重新创建ActivityFragment对象。如果需要进行昂贵的计算来重新创建内部状态,则在Bundle中保存某些内容是有意义的。如果您不做任何事情,Android将始终保存Intent,因此您可以不使用Bundle机制而存活。
  • 当发生配置更改时,Android允许您通过在内存中保留对象来优化用户体验。在这里,Activity的生命周期方法得到了涉及,并且由您的实现来有效地使用保存的数据。对于Fragment,这就是setRetainInstance发挥作用的地方:如果设置了它,您的Fragment将在内存中生存于配置更改之后。

我编辑了我的问题以进一步澄清。 我的主要关注点是当我的托管进程被杀死并从内存中移除时,碎片实例会发生什么。顺便说一句:onRetainNonConfigurationInstance已经过时了。 - Rafael Winterhalter
非常感谢您详细的回答,现在这个问题更加清晰易懂了。 - Rafael Winterhalter
我可能要补充一点:实际上,这就是我最初认为它的工作方式,但是setRetainInstance的javadoc条目说控制片段实例是否在Activity重新创建(例如从配置更改)时保留让我感到困惑,因为它只是谈到了_重新创建_。我把它理解为这会影响任何片段的重新创建。onRetainNonConfigurationInstance的条目更清晰明了。更不用说你上面的解释了。所以再次感谢! - Rafael Winterhalter
1
@raphw 很高兴你觉得我的文本易读。Android文档有时会让人感到困惑(甚至误导)。如果我没有从一本书中学到这个,我想我会花费数小时来理解它... - class stacker
你不记得这本书的标题是什么了吗? - Rafael Winterhalter
1
@raphw 我所知道的最好的Android书籍之一,实战Android,第3.3.2节。 - class stacker

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