当内存不足时,Android不会从堆栈中查杀活动

时间:2012-07-23 16:26:56

标签: android android-activity out-of-memory

我们一直在开发一个具有下拉式仪表板的应用程序,该仪表板允许用户在整个应用程序中导航。导航不是很标准,因为几乎每个活动都可以访问此菜单。 在使用菜单播放一段时间后,堆栈开始增长和增长。

所有这些活动都包含里面有几个图像视图的列表视图,每个活动大约需要3mb。如果用户玩得足够并在堆栈上创建了超过25个活动,则会发生以下情况:

  1. 抛出内存不足错误(堆已增加,直到没有堆)。
  2. 由于异常而显示对话框(不幸的是,%activity%已停止。)
  3. 完成了抛出异常错误的活动。
  4. 堆栈中的所有活动都已完成,但历史记录已保留,因此可以进行备份,并且操作系统会自动重新创建每个活动。
  5. 我期待系统在抛出OutOfMemoryError之前自动终止堆栈中最旧的活动......

    为了确保操作系统不会杀死旧活动,我创建了一个每次分配1mb的测试应用程序。猜猜是什么:行为是一样的,抛出了错误的错误:

    问题是:如果需要,我们如何告诉Android操作系统允许释放活动及其资源,以便我们不会得到“不幸的是,您的活动已停止”。对话框?

    概念证明

        package com.gaspar.memorytest;

    import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class MemoryTestActivity extends Activity { /** Called when the activity is first created. */ private byte[] mData; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main1); ((Button)findViewById(R.id.button)).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub Intent i = new Intent(MemoryTestActivity.this, MemoryTestActivity.class); startActivity(i); } }); mData = new byte[1*1024*1024]; } }

5 个答案:

答案 0 :(得分:13)

  

我期待系统在抛出OutOfMemoryError之前自动杀死堆栈中最旧的活动

Android不会这样做。 Android终止进程以释放其他进程的系统内存。它不会以类似的方式涉及应用内部内存使用。

  

我们如何告诉Android操作系统,如果需要,可以释放活动及其资源,这样我们就不会得到“不幸的是,您的活动已停止”。对话框?

你不能。

相反,您需要将应用程序设计为使用较少的活动,或者为每个活动使用较少的资源。例如,您可以通过FLAG_ACTIVITY_REORDER_TO_FRONT“回收”现有活动实例,或者(如Tornquist先生所指出的那样),您可以自己手动finish()个活动。

  

使用菜单播放一段时间后,堆栈开始增长和增长。

您可能应该在这些菜单项上使用FLAG_ACTIVITY_REORDER_TO_FRONT,以便在任务中将现有活动转发,而不是每次都创建新活动。

答案 1 :(得分:1)

当您开始新活动时调用finish()将取消分配您要离开的活动。这将阻止您使用后退按钮访问它,但它应该保持内存不足。

答案 2 :(得分:0)

根据android开发者指南,我还认为系统会杀死旧活动来释放内存:

“当系统停止您的某个活动时(例如新活动启动或任务移至后台),系统可能会在需要恢复系统内存时完全销毁该活动。发生这种情况时,请参阅活动状态丢失。如果发生这种情况,系统仍然知道活动在后台堆栈中有一个位置,但是当活动被带到堆栈顶部时,系统必须重新创建它(而不是恢复它)。为了避免丢失用户的工作,您应该通过在您的活动中实现onSaveInstanceState()回调方法来主动保留它。“

见链接: android activities

答案 3 :(得分:0)

使用意图标志FLAG_ACTIVITY_REORDER_TO_FRONT

 Intent i = new Intent(ActivityD.this, ActivityA.class);
  i.setFlags(FLAG_ACTIVITY_REORDER_TO_FRONT);
 startActivity(i);

这将简单地将ActivityA带到堆栈的前面,并将B和C留在我认为是你想要的地方。那么你可以在D上调用finish(),如果你想从堆栈中删除它。

答案 4 :(得分:0)

CommonsWare's answer是正确的,除了您可以尝试使用此策略来释放活动的内存在最底层:

  • 删除alls片段和/或setContentView(null)
  • onResume:调用Activity#recreate(),或者只是创建新的片段实例并将其重新添加到视图层次结构中。

重要的一点是,不要依赖这些事件onLowMemoryonTrimMemory。因为仅当整个系统在低内存上运行时才调用它们,所以您的应用程序可能在此之前耗尽内存。您必须跟踪进程的内存使用情况,并在进程占用过多内存的情况下采取适当的措施:

val maxMemory = Runtime.getRuntime().maxMemory()
val freeMemory = Runtime.getRuntime().freeMemory()
val totalMemory = Runtime.getRuntime().totalMemory()
val remainMemoryPercent = (freeMemory + maxMemory - totalMemory) * 100f / maxMemory

// check remainMemoryPercent and take action