无法保留嵌套片段
Posted
技术标签:
【中文标题】无法保留嵌套片段【英文标题】:Can't retain nested fragments 【发布时间】:2013-01-28 19:45:30 【问题描述】:还有另一种保存嵌套片段状态的方法吗? 或者如果我们不应该这样做,为什么?谢谢!
02-13 11:42:43.258: E/androidRuntime(7167): java.lang.IllegalStateException: Can't retain fragements that are nested in other fragments
02-13 11:42:43.258: E/AndroidRuntime(7167): at android.support.v4.app.Fragment.setRetainInstance(Fragment.java:742)
【问题讨论】:
嗯。我没有像你这样的例外。对我来说,父项中的 onCreateView 只是从 findFragmentById 获取一个新的子片段。这意味着它需要重新初始化,这对于 Google MapFragment 来说真的很慢。 :( 请注意,在code.google.com/p/android/issues/detail?id=197271修复后,这不再是问题 在支持库24中发布 这仍然是个问题。 【参考方案1】:您可以使用FragmentManager.saveFragmentInstanceState(Fragment)
检索片段状态。返回值实现了 Parcelable,所以你可以把它放在一个 Bundle 中。
对于恢复,您可以使用Fragment.setInitialSavedState(Fragment.SavedState)
提供创建片段后的状态。
【讨论】:
看源码,这个并没有解决这个问题。setRetainInstance
允许您保留任意类字段,甚至是不可打包、不可序列化的字段。但是,此方法仅在您的片段上调用 onSaveInstanceState(Bundle)
,这意味着您所能做的就是保存可以放入 Bundle 中的内容。
@Matthias 确实,但在大多数的情况下,这正是您所需要的。经常使用setRetainInstance
,而不是实施适当的状态管理。
据我所知,如果在父片段上设置保留实例为真,则实际上不需要保留内部片段。为什么 ?因为它也会自动保留孩子。
@Andrew 这就是为什么在嵌套片段中尝试setRetainInstance
时会得到:unable to retain the instance of the children of nested fragments
【参考方案2】:
由于支持库 20+ (https://code.google.com/p/android/issues/detail?id=74222),子片段的实例重新创建存在错误,有一个修复 - http://ideaventure.blogspot.com.au/2014/10/nested-retained-fragment-lost-state.html
来自网页的代码(将此添加到您的父片段) -
private FragmentManager childFragmentManager() //!!!Use this instead of getFragmentManager, support library from 20+, has a bug that doesn't retain instance of nested fragments!!!!
if(mRetainedChildFragmentManager == null)
mRetainedChildFragmentManager = getChildFragmentManager();
return mRetainedChildFragmentManager;
@Override
public void onAttach(Activity activity)
super.onAttach(activity);
if (mRetainedChildFragmentManager != null)
//restore the last retained child fragment manager to the new
//created fragment
try
Field childFMField = Fragment.class.getDeclaredField("mChildFragmentManager");
childFMField.setAccessible(true);
childFMField.set(this, mRetainedChildFragmentManager);
catch (NoSuchFieldException e)
e.printStackTrace();
catch (IllegalAccessException e)
e.printStackTrace();
@Override
public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setRetainInstance(true);
【讨论】:
同上,请不要使用反射。或者至少证明这是唯一的方法。 @NecipAllef,和上面一样——如果你说事情不好——提供更好的解决方案。只有当你知道它存在时才投反对票。【参考方案3】:在AOSP commit之后,这不再是最新支持库的限制。
以下是提交信息:
在嵌套片段上允许 setRetainInstance(true)
将配置更改中的任意嵌套片段保存为 非配置对象。这允许使用保留实例 子片段作为其他中的任意不透明依赖项 碎片。
【讨论】:
这基本正确,新的*支持库更新适用于 7.0 和 5.0 及更低版本,但在 Android 6.0 上无法正常运行。使用保留片段的策略将变得更加规范,因为在 7.0 上,您只需通过 onSaveInstance 保存 1mb 的数据【参考方案4】:问题: mChildFrgamentManager
正在重新创建 (https://code.google.com/p/android/issues/detail?id=74222)解决方法: 如果片段具有 setRetainInstance(true)
,则保留 mChildFrgamentManager
:
@Override
public void onAttach(Activity activity)
super.onAttach(activity);
if (getRetainInstance())
if (mRetainedChildFragmentManager != null)
try
Field childFMField = Fragment.class.getDeclaredField("mChildFragmentManager");
childFMField.setAccessible(true);
childFMField.set(this, mRetainedChildFragmentManager);
catch (NoSuchFieldException e)
e.printStackTrace();
catch (IllegalAccessException e)
e.printStackTrace();
else
mRetainedChildFragmentManager = getChildFragmentManager();
警告:使用此代码,setRetainInstace
应在 onAttach
之前调用。
P.S:这是@attels 答案的一些改进版本。
【讨论】:
@akshayrajkore “之前”是什么意思?这是一个片段的onAttach
方法。
@NecipAllef 为什么?如果您有一个没有反思的解决方法 - 提出它。如果不是 - 请删除反对票。
@akshayrajkore,我误解了你的问题。 setRetainInstance 应该在片段构造函数中调用。
@NecipAllef 为什么我应该使用Activity
而不是Fragment
?问题不在于使用什么,而在于如何解决问题。
@NecipAllef,这是解决问题的方法——这是事实,因为它解决了问题。这个解决方案有多好 - 是另一个问题。由于没有提供更好的解决方案 - 对可行的解决方案投反对票是一种非常糟糕的感觉。特别是如果你连一个问题都不懂。以上是关于无法保留嵌套片段的主要内容,如果未能解决你的问题,请参考以下文章