35

我从这里关注了“避免内存泄漏”文章。

然而,所提出的解决方案并没有解决泄漏问题。我在 Windows XP (SDK 2.3.1) 上使用 android 模拟器对此进行了测试。我转储了堆并检查了主要活动仍在堆中(我使用了 MAT)

这是我所做的:

  1. 使用 HelloWorldActivity 创建 HelloWorld 应用程序(它没有子视图)
  2. 运行模拟器并启动 HelloWorld 应用程序。
  3. 单击后退键将其关闭。
  4. 导致 DDMS 中的 gc 和转储堆 <-- 在这里我找到了 HelloWorldActivity 实例。
  5. 其中的“GC Roots 路径”显示了以下路径。

HelloWorldActivity <- PhoneWindow$DecorView <- InputMethodManager

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

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

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

4

3 回答 3

20

似乎调用 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)
    {
    }
}
于 2014-05-27T12:35:59.737 回答
3

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

于 2011-02-18T08:27:33.633 回答
3

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

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

编辑:

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

于 2011-02-18T13:23:07.153 回答