【问题标题】:Main Activity is not garbage collected after destruction because it is referenced by InputMethodManager indirectlyMain Activity 销毁后不会被垃圾回收,因为它被 InputMethodManager 间接引用
【发布时间】:2011-02-18 06:02:37
【问题描述】:

我关注了来自here 的“避免内存泄漏”文章。

但是,建议的解决方案并不能解决泄漏问题。我在 Windows XP (SDK 2.3.1) 上使用 android 模拟器对此进行了测试。我转储了堆并检查了主要活动仍在堆中(我使用了 MAT)

这就是我所做的:

  1. 使用 HelloWorldActivity 创建 HelloWorld 应用(它没有子视图)
  2. 运行模拟器并启动 HelloWorld 应用。
  3. 点击后退键将其关闭。
  4. 导致 DDMS 中的 gc 和转储堆
  5. 'Path to GC Roots' 显示以下路径。

HelloWorldActivity

InputMethodManager 是一个单例和三个对引用 HelloWorldActivity 的 DecorView 的引用。

我不明白为什么 InputMethodManager 即使在 Activity 被销毁后仍然引用 DecorView 实例。

有什么方法可以确保主要活动在关闭后被销毁并可以进行GC?

【问题讨论】:

  • 我已经在两部手机上对此进行了测试,在这两种情况下,Activity(没有覆盖)在后按后都是 GC-ed。
  • 问题中指向文章的链接无效。这是正确的(无论如何我认为这是预期的链接):android-developers.blogspot.com/2009/01/…

标签: android android-activity garbage-collection inputmethodmanager


【解决方案1】:

似乎调用 InputMethodManager 的方法 'windowDismissed' 和 'startGettingWindowFocus' 可以解决问题。

类似这样的:

@Override
protected void onDestroy()
{
    super.onDestroy();
    //fix for memory leak: http://code.google.com/p/android/issues/detail?id=34731
    fixInputMethodManager();
}

private void fixInputMethodManager()
{
    final Object imm = getSystemService(Context.INPUT_METHOD_SERVICE);

    final Reflector.TypedObject windowToken
        = new Reflector.TypedObject(getWindow().getDecorView().getWindowToken(), IBinder.class);

    Reflector.invokeMethodExceptionSafe(imm, "windowDismissed", windowToken);

    final Reflector.TypedObject view
        = new Reflector.TypedObject(null, View.class);

    Reflector.invokeMethodExceptionSafe(imm, "startGettingWindowFocus", view);
}

反射器代码:

public static final class TypedObject
{
    private final Object object;
    private final Class type;

    public TypedObject(final Object object, final Class type)
    {
    this.object = object;
    this.type = type;
    }

    Object getObject()
    {
        return object;
    }

    Class getType()
    {
        return type;
    }
}

public static void invokeMethodExceptionSafe(final Object methodOwner, final String method, final TypedObject... arguments)
{
    if (null == methodOwner)
    {
        return;
    }

    try
    {
        final Class<?>[] types = null == arguments ? new Class[0] : new Class[arguments.length];
        final Object[] objects = null == arguments ? new Object[0] : new Object[arguments.length];

        if (null != arguments)
        {
            for (int i = 0, limit = types.length; i < limit; i++)
            {
                types[i] = arguments[i].getType();
                objects[i] = arguments[i].getObject();
            }
        }

        final Method declaredMethod = methodOwner.getClass().getDeclaredMethod(method, types);

        declaredMethod.setAccessible(true);
        declaredMethod.invoke(methodOwner, objects);
    }
    catch (final Throwable ignored)
    {
    }
}

【讨论】:

  • onStop 应该是一个更好的候选者。
  • @Snicolas 这取决于。 “onStop”只是一个信号,表明您的活动不可见。 “onDestroy”无疑是活动的“出口点”。调用这些方法可能会破坏“onStart”正式后的状态和行为将是未定义的。因此,在将解决方法放在“onStop”中之后,应该进行大量测试。
  • 谢谢!不过,我认为对fixInputMethodManager(); 的调用需要在super.onDestroy(); 之前进行。至少这对我有用。
  • 我在许多活动中成功使用了此修复程序。在包含碎片的一个中,这个技巧没有奏效。您是否也需要将此添加到片段的 onDestroy 中?
  • "windowDismissed" 似乎就足够了。为什么开始GettingWindowFocus?
【解决方案2】:

如果我正确理解您的问题,答案是:不,您无法确保该活动已被 gc'ed。您的活动的 onDestroy() 方法应该已被调用并且活动已关闭。然而,这并不意味着进程被杀死或活动被 gc'ed;由系统管理。

【讨论】:

  • 实际上,当处于匆忙中或处于压力之下时,人们往往会遵循工作流程“活动开始-停止-hprof-dump”。但不要忘记,您无法知道 GC 何时会发生,因此您的“泄露”活动可能会在 3 分钟内被 gc'ed,但再次,您检查了未 gc'ed 堆转储......那是反正我的情况。
  • 你总是可以在做 GC 后进行转储,这很有帮助
【解决方案3】:

我注意到一些听众倾向于在某些情况下保留对活动的引用,即使在活动应该已经完成​​之后也是如此。例如,从纵向到横向的旋转可以重新启动您的 Activity,如果您不幸的是,您的第一个 Activity 没有正确 gc-ed(在我的情况下,由于一些听众仍然持有对它的引用)。

作为一名前 C/C++ 程序员,我已将它植入我的脊椎,以“取消设置”Activity.onDestroy() (setXyzListener(null)) 中的所有侦听器。

编辑:

正如 Ted 在下面评论的那样,确实应该分别在 Activity.onResume()Activity.onPause() 中“设置”和“取消设置”侦听器。

【讨论】:

  • 这是正确的想法。不过,一般来说,注销监听器的正确时间是在onPause(),而不是onDestroy()。 (这也意味着侦听器应该在onResume() 中注册,而不是在生命周期的早期。)这是为什么?在onPause() 返回后,无法保证您的活动会收到任何进一步的回调。 (也就是说,您的活动在onPause() 返回后是可终止的。)
  • @Ted Hopp:很棒的评论!我已经相应地改变了我的答案。
  • @Ted 谢谢。在我的情况下,我发现 InputMethodManager 即使在完成后仍将活动的 DecorView 作为当前根视图引用。请注意,活动是空的(只是扩展活动,没有覆盖),我没有为它设置任何侦听器。我是否必须做一些事情才能从 InputMethodManager 注销活动?如果是这样,我该怎么做?
  • 实际上,我不知道您应该做些什么。正如您所提到的:这是一个空活动,您自己没有主动添加任何侦听器或其他依赖项。每个现有的依赖项都是由“系统”添加的,那么我认为期望“系统”也删除这些依赖项是公平的。您是否从更长远的角度测试了这种情况?如果你等待几分钟会发生什么?例如,5 分钟后,您的 Activity GC 了吗?如果您经常使用手机/模拟器怎么办?内存泄漏真的是一致的内存泄漏吗?
猜你喜欢
  • 2016-09-16
  • 1970-01-01
  • 1970-01-01
  • 2017-11-02
  • 1970-01-01
  • 1970-01-01
  • 2017-01-19
  • 2012-07-19
  • 1970-01-01
相关资源
最近更新 更多