Java 内存泄漏-Android.os.Message持有的Android活动

Java 内存泄漏-Android.os.Message持有的Android活动,java,android,memory-leaks,Java,Android,Memory Leaks,我正在开发一个应用程序,其中90%的活动继承自一个公共活动,所有这些活动都会泄漏,这意味着如果我从a->B开始,然后B->a(finish()被调用),B的onDestroy()会被调用,但它仍然泄漏(用MAT检查) 泄漏的活动相当大(10MB~),因此在来回运行几次后,应用程序与OOM崩溃 我检查了堆转储,并按照GC根的路径查找泄漏活动,它们都是这样的: 所以我猜是普通超类中的某些东西在泄漏。我已经检查过,当活动被销毁时,所有的BroadcastReceiver和Listener都已注销,并

我正在开发一个应用程序,其中90%的活动继承自一个公共活动,所有这些活动都会泄漏,这意味着如果我从
a->B
开始,然后
B->a
finish()
被调用),
B
onDestroy()
会被调用,但它仍然泄漏(用MAT检查)

泄漏的活动相当大(10MB~),因此在来回运行几次后,应用程序与OOM崩溃

我检查了堆转储,并按照GC根的路径查找泄漏活动,它们都是这样的:

所以我猜是普通超类中的某些东西在泄漏。我已经检查过,当活动被销毁时,所有的BroadcastReceiver和Listener都已注销,并且没有使用
处理程序
s,也没有可能导致泄漏的匿名内部类(至少在我看来是这样)

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

编辑

我发现有两段代码被注释掉后,活动不再泄漏:

  • 实例化
    ProgressDialog
    的一些代码行
  • 使用匿名
    Runnable
    调用
    postDelayed
  • 在第一种情况下,对话框的
    dismise()
    函数在销毁之前被调用,所以我不知道为什么会出现问题。在第二种情况下,
    onPause
    中的
    Runnable
    调用removeCallbacks,因此理论上它是正确清理的,不是吗


    干杯。

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

    @Override
    protected void onResume() {
        ...
        mCallback = new Runnable() { ... };
        getContentView().postDelayed(mCallback, mDelay);
    }
    
    令人惊讶的是,在
    onPause()
    中删除了此回调:

    为什么这没有阻止泄漏对我来说仍然是个谜。起初,我尝试使用
    getContentView().getHandler().removeCallbacksAndMessages(null)
    ,它修复了漏洞,但随后该应用程序在看似无关的地方将内容完全分解


    最后,修复漏洞的方法是创建一个
    处理程序
    实例,并在此处理程序上调用
    postDelayed()
    removeCallbacks()

    我的第一个快照将是一个匿名或内部类,它保留对活动上下文的引用。发布你的活动代码,以便我们可以查看。不幸的是,我不能透露代码,它非常庞大和混乱。MAT屏幕截图告诉你什么了吗?它告诉我你正在泄漏上下文,它被一个消息队列持有。首先,验证活动中是否存在任何非静态内部类。非静态内部类中的对象持有对其父类的引用,因此您可能需要对其进行双重检查。还要再次检查您发布的runnable:运行时间是否太长?只要它在消息队列中,活动的上下文就不会被破坏。记录
    getContentView.postDelayed
    getContentView.removeCallbacks
    返回的布尔值会很有趣。在我看来,在视图中公开这些方法是一个非常糟糕的API设计。我会把这两个方法只留在Handler类中。这也让我觉得处理程序的实现可以更加健壮,以防它是另一个。我确实检查了
    removeCallbacks
    的返回值,它总是返回true,对我来说似乎没有什么明显的错误。我同意API的设计有点不可靠,不知道为什么这些函数用于。。。
    @Override
    protected void onPause() {
        ...
        getContentView().removeCallbacks(mCallback);
        mCallback = null;
    }