web-dev-qa-db-fra.com

Avertissement: ne placez pas les classes de contexte Android dans des champs statiques; c'est une fuite de mémoire (et casse également Instant Run)

Android Studio:

Ne placez pas Android classes de contexte dans des champs statiques; c'est une fuite de mémoire (et casse également Instant Run)

Donc 2 questions:

# 1 Comment appelez-vous startService à partir d'une méthode statique sans variable statique pour le contexte?
# 2 Comment envoyez-vous une diffusion locale depuis une méthode statique (identique)?

Exemples:

public static void log(int iLogLevel, String sRequest, String sData) {
    if(iLogLevel > 0) {

        Intent intent = new Intent(mContext, LogService.class);
        intent.putExtra("UPDATE_MAIN_ACTIVITY_VIEW", "UPDATE_MAIN_ACTIVITY_VIEW");
        mContext.startService(intent);
    }
}

ou

        Intent intent = new Intent(MAIN_ACTIVITY_RECEIVER_INTENT);
        intent.putExtra(MAIN_ACTIVITY_REQUEST_FOR_UPDATE, sRequest));
        intent.putExtra(MAIN_ACTIVITY_DATA_FOR_VIEW, sData);
        intent.putExtra(MAIN_ACTIVITY_LOG_LEVEL, iLogLevel);
        LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);

Quelle serait la bonne façon de faire cela sans utiliser mContext?

NOTE: Je pense que ma principale question pourrait être de savoir comment passer du contexte à une classe de la méthode appelante.

76
John Smith

Il suffit de le transmettre en tant que paramètre à votre méthode. Il n'y a aucun sens à créer une instance statique de Context uniquement dans le but de démarrer un Intent.

Voici à quoi devrait ressembler votre méthode:

public static void log(int iLogLevel, String sRequest, String sData, Context ctx) {
    if(iLogLevel > 0) {

        Intent intent = new Intent(ctx, LogService.class);
        intent1.putExtra("UPDATE_MAIN_ACTIVITY_VIEW", "UPDATE_MAIN_ACTIVITY_VIEW");
        ctx.startService(intent);
    }
}

Mise à jour à partir des commentaires sur la question: Cascade le contexte de l'activité initiatrice (via les paramètres du constructeur ou les paramètres de la méthode) jusqu'au point où vous en avez besoin.

Assurez-vous simplement que vous transmettez context.getApplicationContext () ou appelez getApplicationContext () sur tout contexte transmis au moyen de méthodes/constructeur à votre singleton si vous décidez de le stocker dans un champ membre.

exemple idiot (même si quelqu'un passe dans une activité, il va saisir le contexte de l'application et l'utiliser pour instancier le singleton):

public static synchronized RestClient getInstance(Context context) {
    if (mInstance == null) {
        mInstance = new RestClient(context.getApplicationContext());
    }
    return mInstance;
}

getApplicationContext () selon la documentation: "Renvoie le contexte de l'objet Application global unique du processus en cours."

Cela signifie que le contexte renvoyé par "getApplicationContext ()" restera actif tout au long du processus. Par conséquent, peu importe si vous stockez une référence statique, où que ce soit, car il sera toujours présent pendant l'exécution de votre application (et survivre à tous les objets)./singletons instancié par elle).

Comparez cela au contexte dans les vues/activités contenant de grandes quantités de données. Si vous divulguez le contexte d'une activité, le système ne pourra pas libérer cette ressource, ce qui n'est évidemment pas bon.

Une référence à une activité en fonction de son contexte doit avoir le même cycle de vie que l'activité elle-même, sinon le contenu du dossier sera pris en otage, ce qui provoque une fuite de mémoire (ce qui explique l'avertissement de présence de charpie).

EDIT: Pour le gars dénigrant l'exemple de la documentation ci-dessus, il y a même une section de commentaires dans le code à propos de ce que je viens d'écrire à propos de:

    // getApplicationContext() is key, it keeps you from leaking the
    // Activity or BroadcastReceiver if someone passes one in.
48
Marcus Gruneau

C'est juste un avertissement. Ne t'inquiète pas. Si vous souhaitez utiliser un contexte d'application, vous pouvez l'enregistrer dans une classe "singleton", utilisée pour enregistrer toutes les classes singleton de votre projet.

4
Licat Julius

Dans votre cas, cela n’a pas beaucoup de sens de l’avoir comme champ statique, mais je ne pense pas que ce soit mauvais dans tous les cas. Si vous faites maintenant ce que vous faites, vous pouvez avoir un champ statique qui a un contexte et le rendre nul plus tard. Je crée une instance statique pour ma classe de modèle principale qui a un contexte à l'intérieur, son contexte d'application pas un contexte d'activité et j'ai également un champ d'instance statique de classe contenant une activité que je null dans détruire. Je ne vois pas que j'ai une fuite de mémoire. Donc, si un type intelligent pense que je me trompe, n'hésitez pas à commenter ...

Instant Run fonctionne aussi très bien ici ...

2
Renetik

En règle générale, évitez de définir les champs de contexte comme statiques. L'avertissement lui-même explique pourquoi: c'est une fuite de mémoire. La pause instantanée pourrait ne pas être le plus gros problème de la planète .

Maintenant, il existe deux scénarios dans lesquels vous obtiendriez cet avertissement. Pour un exemple (le plus évident):

public static Context ctx;

Et puis il y a le peu plus délicat, où le contexte est encapsulé dans une classe:

public class Example{
    public Context ctx;
    //Constructor omitted for brievety 
}

Et cette classe est définie comme statique quelque part:

public static Example example;

Et vous aurez l'avertissement.

La solution elle-même est assez simple: Ne placez pas de champs de contexte dans des instances statiques , que ce soit une classe d'habillage ou la déclarant directement statique.

Et la solution à l'avertissement est simple: ne placez pas le champ de manière statique. Dans votre cas, transmettez le contexte en tant qu'instance à la méthode. Pour les classes dans lesquelles plusieurs appels de contexte sont effectués, utilisez un constructeur pour transmettre le contexte (ou une activité) à la classe.

Notez que c'est un avertissement, pas une erreur. Si pour une raison quelconque vous avez besoin d’un contexte statique, vous pouvez le faire. Bien que vous créiez une fuite de mémoire lorsque vous le faites.

1
Zoe

Si vous vous assurez que c'est un contexte d'application. Ça n'a pas d'importance. Ajoute ça

@SuppressLint("StaticFieldLeak")
1
Victor Choy