Android:OutOfMemory 错误和后台堆栈

Posted

技术标签:

【中文标题】Android:OutOfMemory 错误和后台堆栈【英文标题】:Android: OutOfMemory error and the backstack 【发布时间】:2013-12-26 11:13:45 【问题描述】:

下面的表格代表了这个问题所涉及的应用程序中的工作流程。

我遇到了 OutOfMemory 错误的问题,主要是因为用户能够多次从活动 B 切换到活动 D(他们每次尝试都显示不同的内容),而之前的活动没有被破坏。这会导致一个非常大的 backstack,从而导致 OutOfMemory 错误。

为避免这种情况,我按照建议将父活动添加到清单并使用 TaskStackBuilder 类创建新的后台堆栈:

void openDFromB()

    Intent i = new Intent(this, ActivityD.class);
    TaskStackBuilder.create(this)
        .addParentStack(ActivityD.class)
        .addNextIntent(i)
        .startActivities();

如果用户现在从活动 B 切换到 D,则 MainMenu、A 和 B 被销毁,并创建一个新的 Backstack(MainMenu、C、D)。 这解决了我的 api 级别大于 10 的内存问题,但不幸的是 TaskStackBuilder 不会为 api 11 之前的设备创建 backstacks。

知道如何避免用户在 api 11 之前堆积无限量的活动吗?这是否可能,或者我应该尝试在 onPause 中释放尽可能多的资源,以便在 OoM 之前获得最大数量的堆叠活动?

提前致谢!

【问题讨论】:

【参考方案1】:

试试这个-

Intent intent = new Intent(getApplicationContext(),yourActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);

另一种解决方案是-

在清单文件中的 Activity 上设置 android:noHistory="true" 将在导航离开时从堆栈中删除该 Activity。

但最合适的方法是在您开始新活动时调用finish() 方法,如以下示例代码所示-

Intent intent = new Intent(this, yourActivity.class);
startActivity(intent);
finish();

您还可以创建自定义广播接收器并将其注册到可以在您选择的事件上触发的每个活动中。 For more help you can check this link.

【讨论】:

【参考方案2】:

Zabri 的答案很好,但我建议另一个可能更好: 您不会关闭每个活动,但是当您打开之前已打开的新活动时,您会告诉系统它可以清除返回的路径并重新打开该活动,当然还有新的意图和您需要的所有新数据.

所以,如果你做 A - B - D - B - D - B,而不是拥有那么大的筹码,你将拥有

    A - B A - B - D A - B A - B - D A - B

所以,如果你做 C - D - B - D - B - D,你的堆栈将是

    C - D C - D - B C - D C - D - B C - D

等等。 通过这种方式,我认为堆栈实际上反映了用户可以想到的内容,并且导航非常符合逻辑,您甚至不需要捕获后退按钮即可使导航有意义,(当然,如果您可以这样做您想要更具体的用户体验)

实现这一点的代码是: 创建一个接收类的函数(类似于 ActivityD.class),在那里你创建一个意图,带有标志 Intent.FLAG_ACTIVITY_CLEAR_TOPIntent.FLAG_ACTIVITY_NEW_TASK,然后当你需要打开一个活动时,只需调用这个函数。您不关心活动是否在堆栈中。如果不是,则正常创建。

 protected void startActivity(Class<?> clase) 
    final Intent i = new Intent(this, clase);
    int flags = Intent.FLAG_ACTIVITY_CLEAR_TOP;
    flags |= Intent.FLAG_ACTIVITY_NEW_TASK;
    i.setFlags(flags);
    startActivity(i);



startActivity(ActivityD.class);

【讨论】:

【参考方案3】:

可能是您的应用程序存在内存泄漏,并且由于较旧的活动没有被 GC 回收,最终每次活动切换发生时堆大小都会增加。

如何发现泄漏? 请关注:http://www.youtube.com/watch?v=_CruQY55HOk

参考文献:

    http://android-developers.blogspot.in/2011/03/memory-analysis-for-android.html. http://kohlerm.blogspot.in/2009/07/eclipse-memory-analyzer-10-useful.html

【讨论】:

Activity 永远不会被 Android 销毁以回收堆空间。这是一个常见的误解。只有当应用程序暂停并且系统内存不足时,它才会破坏活动。【参考方案4】:

我不知道它如何与 Navigation Drawer 一起工作,但我前段时间正在创建一个 Android 应用程序,它从加载屏幕开始,然后移至应用程序的主要活动。由于我不需要/不希望用户返回加载屏幕,所以我只是在转到主要活动之前调用了finish()。像这样:

Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
finish();

然后,当用户从主 Activity 按下 返回 按钮时,他们并没有到达加载 Activity,而是到达了他们之前所在的任何地方。

所以你也可以尝试这样做:

void openDFromB()

    Intent i = new Intent(this, ActivityD.class);
    startActivity(i);
    finish();

正如我所说,我从未实现过导航抽屉,但有件事告诉我这样做可能会破坏它的整个行为。此外,在活动中调用finish() 是一个非常基本的概念,如果您还没有尝试过,我会感到惊讶。 :) 但是,我还是把它贴在黑暗中,以防万一它对你有帮助。祝你好运。 :)

【讨论】:

【参考方案5】:

您好,您可以尝试在您的清单文件中的应用程序标签下添加以下语句:

android:largeHeap="true"

还要确保您的 android:targetSdkVersion 是 14。

【讨论】:

以上是关于Android:OutOfMemory 错误和后台堆栈的主要内容,如果未能解决你的问题,请参考以下文章

Android Studio - build.gradle 未捕获翻译错误 ExecutionException OutOfMemory 中的问题

android 内存问题

使用 PNG 图像的 SplashScreen 导致 Android.Views.InflateException 然后是 OutOfMemory

如何修复 Java 中的 OutOfMemory 错误?

尝试使用 ZipFileSet 提取大 jar 时出现 OutOfMemory 错误

Xamarin:Android - 带有大位图的OutOfMemory异常 - 如何解决?