在onSaveInstanceState()

时间:2018-10-25 21:47:18

标签: android memory-leaks android-viewpager parcelable

在某些活动中,我必须将MVC模型另存为可打包文件。这个可包裹的包裹是根据the doc构建的,我对它的阅读已经足够多了(但是谁知道,我显然可能错过了一些东西)。该活动存在漏洞,但我正在努力了解其原因。 SO问题Communication objects between multiple fragments in ViewPager很有趣,但是我的代码已经遵循了答案中的指导原则。

该活动拥有一个Viewpager,其中包含大约60个片段(但最多350个)。将模型从活动传递到所有片段,并将片段中的用户操作保存到模型中。

每当我暂停活动时,就会一次触发onSaveInstanceState,并在多次触发包裹的writeToParcel方法之后立即触发。触发器的数量取决于viewpager + 1中加载的片段数。因此,在活动启动时,如果我关闭仿真器然后再打开,writeToParcel被调用3次(仅1次和1次)。如果加载了第2个片段),如果我向右滑动一次并再次执行,则调用4次(显示第2个片段,并加载第3个片段),如果我在适配器上setExtPosition()并转到第10个片段,{ {1}}被调用7次(加载了9th,10th和11h)。

当然,如果我的用户轻扫每个片段,最终将得到一个丑陋的writeToParcel,这将我带到这里。

这是一些代码。这里可能会有大量的代码/概念改进,非常欢迎任何提示,但是我的主要问题是我发现的这个肮脏的小泄漏。

在我的活动中:

TransactionTooLargeException

在我的片段中:

@Override
public void onSaveInstanceState (Bundle outState) {
    outState.putParcelable("model", myParcelable);
    super.onSaveInstanceState(outState);
}

在我的可包裹模型中:

public static MyFragment newInstance(Model model) {
    MyFragment fragment = new MyFragment();
    Bundle args = new Bundle();
    args.putParcelable(KEY_MODEL, model);
    fragment.setArguments(args);
    return fragment;
}

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Bundle args = getArguments();
    mModel = args.getParcelable(KEY_MODEL);
}

我在@Override public void writeToParcel(Parcel dest, int flags) { int startSize = dest.dataSize(); dest.writeString(foo); //one of these string is supposed to be null dest.writeString(bar); dest.writeString(foobar); dest.writeByte((byte) (isMyObjectTrue ? 1 : 0)); dest.writeList(someStringList); dest.writeList(someIntegerList); dest.writeBundle(someBundle); //I use a Bundle to save a Map<String,String> int endSize = dest.dataSize(); } 方法中运行了调试器,但我惊讶地发现writeToParcel()永远不会为0。这正常吗?

我搜索了整个代码,仅在此活动和片段startSize中调用putParcelable()或任何名称可拆分的书写方法。

如何找到这种怪异的指数行为的原因?

PS:当然可以要求更多代码。

编辑

我已经实现了@Ben P.建议的解决方案,该问题已得到很大改善,但尚未完全解决。我的活动实现了一个接口,该接口现在具有在newInstance()中调用的getModel()方法,并且使用了onAttach()从片段更新模型。片段的setUserInput(char userInput)方法不再保存模型。

MyFragment

newInstance()

这把指数问题变成了线性问题,这显然更好,但仍然是一个问题。

现在,@Override public void onAttach(Context context) { super.onAttach(context); try { callBack = (MyInterface) context; //callBack is a class field mModel = callBack.getModel(); //mModel too } catch (ClassCastException e) { throw new ClassCastException(context.toString() + " must implement MyInterface"); } } 仅被调用一次,但是总包裹大小仍随着装入的物品数而增加。根据{{​​1}}的测量,我的模型在包裹内大约占3kb(+/- 10%,取决于输入的数量)。

我怎么知道增长来自哪里?

1 个答案:

答案 0 :(得分:1)

在具体讨论您的问题之前,我想指出传递给Bundle的{​​{1}}是片段实例状态的一部分。每次销毁并重新创建一个片段时,都必须保留这些参数。因此,您在setArguments()中添加的任何内容都有可能在配置更改期间被打包和取消打包。


  

该活动拥有一个Viewpager,其中包含大约60个片段(但最多350个)。将模型从活动传递到所有片段,并将片段中的用户操作保存到模型中。

这听起来像您有一个Bundle对象,所有片段都共享。如果是这种情况,我建议将模型对象作为其参数Model的一部分传递给每个片段。保存和还原实例状态时,这样做将导致大量重复。相反,我会在您的Bundle中公开一个方法(类似Activity),然后从您的片段中调用该方法以检索模型实例。

另一方面,听起来您可能只是从同一对象getModel()开始 ,并且每个片段都可以以某种方式对其进行突变。这意味着您要做必须为每个片段保存一些东西到实例状态...但是您可以在这里进行优化。不必保存和还原整个Model对象,您可以只存储差异。也就是说,如果片段#1更改了模型的Model,而片段#2更改了模型的name,那么您可以让片段#1仅保存新名称,让片段#2仅保存新名称。值。这样做而不是保存两个额外的模型对象副本,可能会节省大量资金。