Android - 使用ViewPager>内存压力崩溃片段>活动

时间:2013-05-20 19:35:17

标签: android memory-management android-fragments android-viewpager activity-finish

我在我们的应用程序中使用v4支持库,我在应用程序中有一个基于步骤的过程,在正常情况下正常工作。但是,我们要求一切都需要工作,而不是在内存压力情况下崩溃。所以我正在使用SetAlwaysFinish库来帮助解决这个问题(https://github.com/bricolsoftconsulting/SetAlwaysFinish)。这对于确定需要检测和处理这些情况的区域非常有帮助,但我遇到了一个让我难过的问题。请记住,这在正常情况下工作正常,但我明确地使用“setAlwaysFinish”= ON进行测试。

这是我的设置:我有一个“ProcessActivity”类,它承载ViewPager的布局。 ViewPager有一个适配器集,其中包含我将包含该进程的片段列表。这是在onCreate()方法中:

ProcessActivity

createSteps();  // this creates/populates the ArrayList "processSteps" with Fragments
theAdapter = new ProcessAdapter(this, processSteps); // the ProcessAdapter class extends FragmentStatePagerAdapter, with an additional list of Fragment steps
theViewPager.setAdapter(theAdapter);

还有其他部分,但这是它如何设置的核心。在其中一个Fragment步骤中,我实际上需要从步骤过程中“中断”并暂时推送到Activity以执行某些逻辑(然后再返回到步骤过程)。我使用ForResult执行以下操作,因为我需要能够在活动结束时处理活动中的“确定/取消”操作:

Step4Fragment

Intent intent = new Intent(getActivity(), ThePushActivity.class);
intent.putExtra(blah..);
startActivityForResult(intent, THE_REQCODE);

一旦它被推到这个视图上,有时会调用ProcessActivity和Step4Fragment的onDestroy()方法(由于alwaysFinish)。有时似乎工作正常,它回调到Step-Fragment流程。但通常,它所做的是它将调用ProcessActivity的onDestroy()方法,然后它将重新调用onCreate()方法,并填充bundle saved-instance-state。这将调用上面的步骤创建代码,它将应用程序置于一个时髦的状态,用户所在的最后一步显示,但在幕后它实际上是第一步(片段在那时,是打架和断开连接),不可避免地发生崩溃。看来在这一点上,Step4Fragment是完全脱节的,如果你试图做任何事情就会崩溃,即使它看起来好像已经正确地重新实例化了

有关解决此问题的最佳方法的任何想法?我认为如果我找到一种方法甚至只是重置一些内容就可以了,如果发生内存问题,它会将用户踢回到进程的第一步。但是,我当然担心崩溃。

如果我需要提供更多细节,请告诉我。在此先感谢您的帮助!

更新#1: 只是一个快速更新,我注意到片段被重新实例化和初始化,它正确地落入“当前”片段的onActivityResult()方法,并做了它需要正确做的事情。根据其中一个场景,似乎断开连接位于基础ProcessActivity类中。 ViewPager布局显示正确,但ViewPager之外的所有内容都不正确(即它表示它在过程的第一步,当它应该指示它在4号时,并且导航按钮显示为第1步而不是第4)。

所以我猜我需要以某种方式手动正确设置这些元素。在深入研究这个问题时,我可能会遗漏一些人员积极帮助解决这些问题所需的代码。如果我可以以某种方式访问​​ViewPager字段的状态,该字段包含获取“当前显示的片段”的能力,在调用整个onDestroy()/ onCreate()之前,可能来自savedInstanceState包?这可能会解决我的问题。但是通过调试检查这个包,我几乎只看到Fragments本身及其各自的状态。不过,我会继续挖掘。

无论如何,如果有人对如何正确地完成这类事情有任何想法(当然是高层),请告诉我。

更新#2 我看到即使“当前”片段似乎正确启动,并且视图正确显示,一切都是分离的。我无法在getResources()或getActivity()等上调用任何方法。我实际上能够基于将步骤索引(int)保存到savedInstanceState包中,然后重新加载UI,使ProcessActivity正常工作。它周围的元素。但是,我仍然有这个阻止程序,当前的Fragment与Activity分离,即使它似乎正在重新实例化。

更新#3 当我按照这篇文章的方向:ViewPager and fragments — what's the right way to store fragment's state?时,当我尝试执行第一个putFragment()时,我最终会遇到一个直接的异常。例外情况是:“IllegalStateException:片段Step4Fragment当前不在FragmentManager中”。我认为这可能与我只在左边保留一个片段而在任何时候只有一个片段向右“活动”(即offScreenPageLimit)这一事实有关。

2 个答案:

答案 0 :(得分:0)

你确定你的片段不是re-instantiated吗?

  

Fragment的所有子类都必须包含一个公共空构造函数。框架通常会在需要时重新实例化一个片段类,特别是在状态恢复期间,并且需要能够找到此构造函数来实例化它。如果空构造函数不可用,则在状态恢复期间的某些情况下将发生运行时异常。

答案 1 :(得分:0)

原来这个解决方案类似于这篇文章中提出的解决方案:ViewPager and fragments — what's the right way to store fragment's state?

我有几件事需要改变。首先,我需要将Fragments移动到字段而不是临时变量。其次,我需要避免每次在onCreate()中实例化新的Fragment。相反,它应该尝试从savedInstanceState中拉出它们:

fragment1 = (Fragment1Step) getSupportFragmentManager().getFragment(savedInstanceState, Fragment1Step.class.getName());
fragment2 = (Fragment2Step) getSupportFragmentManager().getFragment(savedInstanceState, Fragment2Step.class.getName());
etc...

然后在此之后,我会对每个人进行后续检查;如果它们为空(即进入新鲜而不是来自已保存的实例),那么它们将被实例化为新的:

if (fragment1 == null) {
    fragment1 = new Fragment1Step();
} 
if (fragment2 == null) {
    fragment2 = new Fragment2Step();
} 

为了使其工作,当然,这些也应该在onSaveInstanceState()方法中保存:

try {
    getSupportFragmentManager().putFragment(outState, Fragment1Step.class.getName(), fragment1);
} catch (Exception e) {
    // do nothing
}
try {
    getSupportFragmentManager().putFragment(outState, Fragment2Step.class.getName(), fragment2);
} catch (Exception e) {
    // do nothing
}
etc...

我在try / catch块中有这些的原因是因为我们的ViewPager只有offScreenPageLimit为1,因此如果它们当前不在'active'中,其中一些会在“putFragment()”调用上抛出异常堆栈的碎片正在显示。

它并不是非常漂亮,并且可能有更好的方法来解决这个问题......但是现在这种方法似乎很好用。