10

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

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

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

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

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

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

概念证明

    包 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]; } }

4

6 回答 6

14

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

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

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

你不能。

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

在使用菜单打开活动一段时间后,堆栈开始增长。

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

于 2012-07-23T16:45:14.800 回答
1

finish()在您开始新活动时调用将释放您要离开的活动。这将阻止您使用后退按钮访问它,但它应该会减少内存。

于 2012-07-23T16:31:04.003 回答
0

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

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

见链接: android活动

于 2012-10-05T10:53:33.427 回答
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() 。

于 2017-07-17T10:59:06.160 回答
0

CommonsWare 的回答是正确的,除了你可以尝试这个策略来释放底部堆栈的活动内存:

  • 删除所有片段和/或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
于 2020-04-13T02:48:31.343 回答
0

对于这个问题,我们不能告诉 Android OS 这样做。想想看,这个系统有很多应用程序,如果每个人都这么说,那就糟糕了。但是系统给了我们通知,我们可以用这种方法做到这一点。

//in applicaion class
public void onTrimMemory (int level) {
  //according to different level do different things.
  //for example TRIM_MEMORY_UI_HIDDEN to finish old activities 

}

我们也可以让activity实现ComponentCallbacks,在它自己的类中实现onLowMemeroy,然后当activity开始注册它时,销毁unregister。我们可以ActivityLifecycleCallbacks用来自动实现它。

//in application class
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks(){

   void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState);{
if(activity instanceOf ){
    registerComponentCallbacks((ComponentCallbacks)activity)
  }
}
 
...
void onActivityDestroyed(@NonNull Activity activity);{
  if(activity instanceOf ){
    unregisterComponentCallbacks((ComponentCallbacks)activity)
  }

}


})

// in activity implement ComponentCallbacks, override onlowMemeroy method
void onLowMemory();{
  //finish or remove some resouce
}

至于为什么Android操作系统不能分配,那是因为操作系统有两种内存分配方式。

一种方法是java垃圾分配。它进行对象分配。

另一种方式是low memory kill,保证进程有足够的内存。当内存不足以运行应用程序时,它会在后台杀死其他应用程序。

您在java垃圾分配方式中的活动无法分配,因为它由ActivityThread持有。所以不能配置。

为了释放内存,我们可以在收到内存不足通知时完成旧的活动。

于 2021-12-24T00:24:28.583 回答