【问题标题】:Android - Memory pressure crashes with ViewPager > Fragments > ActivitiesAndroid - ViewPager > Fragments > Activities 导致内存压力崩溃
【发布时间】:2013-05-20 19:35:16
【问题描述】:

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

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

进程活动:

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);

还有其他部分,但这是设置的核心。在其中一个片段步骤中,我实际上需要从步骤过程中“中断”并暂时推出一个活动以执行一些逻辑(然后随后返回到步骤过程)。我使用 ForResult 执行以下操作,因为我需要能够在活动完成时处理活动中的 OK/Cancel 操作:

Step4Fragment:

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

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

对解决这个问题的最佳方法有什么想法吗?我认为如果我能找到一种方法甚至只是重置一些东西,这样如果发生内存问题,它就可以让用户回到流程的第一步,这很好。但是,当然,我最关心的是崩溃。

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

更新 #1: 只是一个快速更新,我注意到片段被重新实例化和初始化,它正确地落入“当前”片段的 onActivityResult() 方法并正确执行它需要执行的操作。在这些场景之一之后,断开连接的位置似乎在基本 ProcessActivity 类中。 ViewPager 布局显示正确,但 ViewPager 之外的所有内容都不正确(即它表明它处于流程的第 1 步,而它应该表明它位于第 4 步,并且导航按钮显示为第 1 步而不是第四个)。

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

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

更新 #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:Fragment Step4Fragment 当前不在 FragmentManager 中”。我认为这可能与我在任何时候只将一个片段保持在左侧和一个片段在右侧“活动”(即 offScreenPageLimit)这一事实有关。

【问题讨论】:

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


    【解决方案1】:

    您确定您的 Fragment 不是 re-instantiated?

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

    【讨论】:

    • 是的,所有的 Fragment 都在被重新实例化,并且它们都有公共的空构造函数。我将日志放入每个日志中,并且每个日志都被调用(它们都在我的帖子中提到的 createSteps() 方法中实例化,在设置它的主流程类的 onCreate() 中......所以他们肯定会被召回)。奇怪的是,从日志看来,一切都准备妥当并被调用以重新设置它停止的视图..但实际上,如果你做任何事情,它不会正确显示并导致崩溃在它显示之后。
    • 在实现更多日志记录之后,我意识到它确实首先调用了空构造函数(这是应该使用的实际构造函数,带有非空附加活动)。但是,在此之后调用 ProcessActivity 类的 onCreate() 时,它会重新创建片段,这些片段似乎是不连贯的片段。我很好奇如何在完成后从 ProcessActivity 类访问由系统正确重新实例化的“真实”片段。我尝试了很多不同的东西,但我没有太多运气。 :(
    • @svguerin3 通过我的个人资料中的网站与我联系。如果您愿意,我会尝试通过 Skype 为您提供帮助。
    • 谢谢詹姆斯!我确实忽略了这篇文章:stackoverflow.com/questions/7951730/…,这表明做我正在做的事情不是一个好主意(从 onCreate() 创建片段)。因此,我将尝试尝试那里提到的解决方案。如果我还没有运气,我一定会联系你的。非常感谢您愿意这样做!我会告诉你进展如何
    • 没问题。我认为这是初始化问题(尽管我认为这是通过非空构造函数)。祝你好运。
    【解决方案2】:

    事实证明,这个问题的解决方案类似于这篇文章中提出的解决方案:ViewPager and fragments — what's the right way to store fragment's state?

    我有几件事需要改变。首先,我需要将片段移动到字段而不是临时变量。其次,我需要避免每次在 onCreate() 中实例化新的 Fragments。相反,它应该尝试将它们从 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,所以如果它们当前不在“活动”中,其中一些将在“putFragment()”调用时抛出异常正在显示的片段堆栈。

    这不是非常漂亮,可能有更好的方法来处理这个问题..但是现在这似乎可以正常工作。

    【讨论】:

      猜你喜欢
      • 2015-08-15
      • 2013-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-11
      • 1970-01-01
      • 2014-11-22
      • 2016-02-21
      相关资源
      最近更新 更多