【问题标题】:Memory leak - Android activities held by android.os.Message内存泄漏 - android.os.Message 持有的 Android 活动
【发布时间】:2014-01-08 17:21:04
【问题描述】:

我正在开发一个应用程序,其中 90% 的活动继承自一个公共活动并且所有这些活动都泄漏,这意味着如果我从 A->B 转到 B->A(调用 finish()),@ 987654325@'s onDestroy() 被调用,但它仍然泄漏(用 MAT 检查)。

泄露的活动非常大(10MB~),所以在来回几次后应用程序因OOM而崩溃。

我检查了堆转储,并沿着 GC 根路径查找泄漏活动,它们看起来都像这样:

所以我猜这是常见的超类中的某些东西正在泄漏。我已经检查了所有 BroadcastReceivers 和 listeners 在活动被破坏时都未注册,并且没有使用 Handlers 也没有可能导致泄漏的匿名内部类(至少在我看来)。

泄漏的原因可能是什么?任何帮助将不胜感激。

编辑

我发现有两段代码在注释掉活动时不再泄漏:

  1. 一些实例化ProgressDialog的代码行。
  2. 通过匿名Runnable 致电postDelayed

在第一种情况下,对话框的dismiss() 函数在销毁之前被调用,所以我不知道为什么这可能是问题所在。第二种情况,是为onPause中的Runnable调用removeCallbacks,所以理论上是正确清理的,不是吗?

干杯。

【问题讨论】:

  • 我的第一个镜头将是一个匿名或内部类,它保持对活动上下文的引用。发布您的活动代码,以便我们查看。
  • 很遗憾,我无法透露代码,而且代码非常庞大且混乱。 MAT 截图有什么告诉你的吗?
  • 它告诉我你正在泄漏上下文并且它被一个消息队列持有。首先,验证您的活动中是否有任何非静态内部类。来自非静态内部类的对象包含对其父级的引用,因此您可能需要仔细检查它。还要仔细检查您发布的可运行文件:运行时间是否太长?只要在消息队列中,activity的上下文就不会被销毁。

标签: java android memory-leaks


【解决方案1】:

问题出在postDelayed 中的匿名Runnable。这个Runnable 是用基础活动的内容视图的postDelayed() 函数调用的,所以它是这样的:

@Override
protected void onResume() {
    ...
    mCallback = new Runnable() { ... };
    getContentView().postDelayed(mCallback, mDelay);
}

令人惊讶的是,这个回调在 onPause() 中被删除了:

@Override
protected void onPause() {
    ...
    getContentView().removeCallbacks(mCallback);
    mCallback = null;
}

为什么这不能防止泄漏对我来说仍然是一个谜。起初我尝试使用 getContentView().getHandler().removeCallbacksAndMessages(null) 并修复了泄漏,但随后应用程序在看似无关的地方完全崩溃了。

最终修复泄漏的是创建一个Handler 实例并在此处理程序上调用postDelayed()removeCallbacks()

【讨论】:

  • 嘿,这真是个谜。记录 getContentView.postDelayedgetContentView.removeCallbacks 返回的布尔值会很有趣。在我看来,在 View 中公开这些方法是一个非常糟糕的 API 设计。我只会将这两个方法留在 Handler 类中。而且也让我觉得 Handler 的实现可以更加健壮,以防万一它是不同的。
  • 我确实检查了 removeCallbacks 的返回值,它总是返回 true,对我来说似乎没有任何问题。我同意 API 设计有点狡猾,不知道为什么这些功能是为了......
猜你喜欢
  • 1970-01-01
  • 2012-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-21
  • 2014-07-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多