34 votes

L'activité principale n'est pas récupérée après sa destruction car elle est référencée par InputMethodManager indirectement.

J'ai suivi l'article "Éviter les fuites de mémoire" de aquí .

Cependant, la solution proposée ne résout pas le problème des fuites. Je l'ai testé avec l'émulateur Android sur Windows XP (SDK 2.3.1). J'ai vidé le tas et vérifié que l'activité principale est toujours dans le tas (j'ai utilisé MAT).

Voilà ce que j'ai fait :

  1. créer une application HelloWorld avec HelloWorldActivity (elle n'a pas de vues enfant)
  2. Exécutez l'émulateur et lancez l'application HelloWorld.
  3. Fermez-la en cliquant sur la touche retour.
  4. Cause gc dans DDMS et dump heap <-- Ici j'ai trouvé l'instance HelloWorldActivity.
  5. Le chemin vers les racines du GC montre le chemin suivant.

HelloWorldActivity <- PhoneWindow$DecorView <- InputMethodManager

InputMethodManager est un singleton et trois références à DecorView qui fait référence à HelloWorldActivity.

Je ne comprends pas pourquoi InputMethodManager continue à référencer l'instance DecorView même après la destruction de l'activité.

Existe-t-il un moyen de s'assurer que l'activité principale est détruite et GC-able après sa fermeture ?

18voto

Denis Gladkiy Points 570

Il semble que les méthodes "windowDismissed" et "startGettingWindowFocus" de l'InputMethodManager soient utilisées.

Quelque chose comme ça :

@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);
}

Le code du réflecteur :

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)
    {
    }
}

3voto

Ted Hopp Points 122617

Si je comprends bien votre question, la réponse est : non, vous ne pouvez pas vous assurer que l'activité est gc'ed. La méthode onDestroy() de votre activité devrait avoir été appelée et l'activité arrêtée. Cela ne signifie pas, cependant, que le processus est tué ou que l'activité est gc'ed ; c'est géré par le système.

3voto

dbm Points 3814

J'ai remarqué que certains listeners ont tendance à garder une référence à l'activité dans certaines circonstances, même après que l'activité soit censée être terminée. Une rotation de portrait à paysage peut, par exemple, redémarrer votre activité et si vous êtes malchanceux votre première activité n'est pas gc-ed correctement (dans mon cas en raison de certains listeners conservant une référence à elle).

En tant qu'ancien programmeur C/C++, je me suis fait implanter dans la colonne vertébrale l'idée de "dérégler" tous les écouteurs dans Activity.onDestroy() ( setXyzListener(null) ).

EDITAR:

Comme Ted l'a commenté ci-dessous, il faut en effet "mettre en place" et "enlever" les écouteurs dans l'application Activity.onResume() y Activity.onPause() respectivement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X