web-dev-qa-db-fra.com

Comment utiliser Leak Canary

Je sais que c’est probablement une question idiote, mais je suis assez novice dans le développement d’Android, et je rencontre actuellement une erreur OutOfMemoryError dans mes applications, que j’ai essayé de déboguer avec MAT, mais il est encore trop difficile de détecter la fuite activités, puis j’ai trouvé LeakCanary, qui semble plus simple et plus facile à utiliser, mais je n’ai trouvé aucun guide pour débutant, étape par étape, sur l’utilisation de Leak Canary, même sur Google. J'ai installé LeakCanary à travers les dépendances de mon build.gradle, et voici ce que j'ai eu jusqu'à présent:

ExampleApplication.Java

public class ExampleApplication extends Application {

    public static RefWatcher getRefWatcher(Context context) {
        ExampleApplication application = (ExampleApplication) context.getApplicationContext();
        return application.refWatcher;
    }

    private RefWatcher refWatcher;

    @Override
    public void onCreate() {
        super.onCreate();
        refWatcher = LeakCanary.install(this);
    }

    final class KeyedWeakReference extends WeakReference<Object> {
        public final String key;
        public final String name;

        KeyedWeakReference(Object referent, String key, String name,
                       ReferenceQueue<Object> referenceQueue) {
            super(checkNotNull(referent, "referent"), checkNotNull(referenceQueue, "referenceQueue"));
            this.key = checkNotNull(key, "key");
            this.name = checkNotNull(name, "name");
        }
    }

    public void watch(Object watchedReference, String referenceName) {
        checkNotNull(watchedReference, "watchReference");
        checkNotNull(referenceName, "referenceName");
        if(debuggerControl.isDebuggerAttached()) {
            return;
        }
        final long watchStartNanoTime = System.nanoTime();
        String key = UUID.randomUUID().toString();
        retainedKeys.add(key);
        final KeyedWeakReference reference =
            new KeyedWeakReference(watchedReference, key, referenceName, queue);
        watchExecutor.execute()

    }
}

Disons que j'ai une activité pour laquelle je veux que LeakCanary regarde un objet

SampleActivity.Java

public class SampleActivity extends Activity implements View.OnClickListener {
    ImageView level001, level002;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.choose_level);

        level001 = (ImageView) findViewById(R.id.level001);
        level002 = (ImageView) findViewById(R.id.level002);

        // Do all kinds of function
        // How do I use LeakCanary to watch these objects ?

    }
}

Maintenant, comment utiliser LeakCanary pour voir quel objet est à l'origine de la fuite de mémoire. Merci pour votre aide et votre assistance.

26
Charas

Le truc sympa à propos des fuites de canaris, c’est son fonctionnement automatisé . Par défaut, il "surveille" déjà les activités mal gérées. Donc, dès la sortie de la boîte, si une activité fuit, vous devriez recevoir la notification.

Sur mon projet, j'ai ajouté une méthode supplémentaire à la Application comme ceci:

public class ExampleApplication extends Application {
    public static ExampleApplication instance;
    private RefWatcher refWatcher;

    @Override
    public void onCreate() {
        super.onCreate();
        instance = this;
        refWatcher = LeakCanary.install(this);
    }

    public void mustDie(Object object) {
        if (refWatcher != null) {
            refWatcher.watch(object);
        }
    }
}

il est donc important de savoir quand il faut collecter les déchets et demander à ce que les objets soient surveillés.

Par exemple, nous utilisons un "fragment de base" avec le code suivant:

@Override
public void onDestroy() {
    super.onDestroy();
    ExampleApplication.instance.mustDie(this);
}

de cette façon, LeakCanary essaie de vérifier si un fragment perd de la mémoire.

Donc, pour que vous puissiez implémenter davantage votre application, vous pouvez/devriez sur des tâches ou instances que vous savez qu'elle devrait être nettoyée, mais vous pensez que cela pourrait ne pas être, et vous ne savez pas où, vous pouvez aussi appeler cela: ExampleApplication.instance.mustDie(object);

et ensuite, vous DEVEZ exécuter l'application, faire pivoter le périphérique et forcer la fuite à se produire, afin que la fuite puisse détecter/analyser la trace de pile et vous fournir des informations précieuses sur la façon de la réparer.

J'espère que ça aide.

16
Budius

J'ai utilisé ici en application

import Android.content.Context;
import Android.support.multidex.MultiDex;
import Android.support.multidex.MultiDexApplication;
import com.squareup.leakcanary.LeakCanary;
import com.squareup.leakcanary.RefWatcher;
import school.my.com.school.BuildConfig;

public class AppController extends MultiDexApplication {

private RefWatcher refWatcher;

public static RefWatcher getRefWatcher(Context context) {
    AppController application = (AppController) context.getApplicationContext();
    return application.refWatcher;
}



@Override
public void onCreate() {
    super.onCreate();
    if(BuildConfig.DEBUG)
        refWatcher = LeakCanary.install(this);

}

}

Vous pouvez utiliser ici Application à la place de MultiDexApplication

1
Nilesh Panchal

J'ai eu la même question sur la façon d'utiliser LeakCanary. Je voulais juste voir un exemple de base sur la façon de le lancer et voir mon premier chemin d'accès à un objet qui a fui.

Comment utiliser LeakCanary

Voici un exemple de base sur la façon de travailler avec LeakCanary: 

Comment utiliser LeakCanary (4 minutes 13 secondes)

L'un des problèmes que j'ai dû surmonter était de savoir que je devais lancer l'application en mode d'exécution normal, par opposition au mode débogage. Je devais également dévier des instructions de base et définir mon fichier build.gradle au niveau de l'application comme suit:

dependencies {
  debugImplementation 'com.squareup.leakcanary:leakcanary-Android:1.5.4'
  releaseImplementation 'com.squareup.leakcanary:leakcanary-Android:1.5.4'
}

Je pense que debugImplementation n'a pas fonctionné pour moi car LeakCanary ignore la détection de fuite lors du débogage . Bien que LeakCanary indique qu'il fournit la version " non opérante" de sa bibliothèque pour l'édition , le débogage ne fonctionnant pas pour moi, j'ai remplacé la releaseImplementation ci-dessus du com.squareup.leakcanary:leakcanary-Android-no-op:1.5.4 recommandé par le com.squareup.leakcanary:leakcanary-Android:1.5.4.

Comme vous pouvez le voir dans la vidéo, un autre problème que j'avais à traiter était de donner un accès en écriture à LeakCanary. J'ai balayé et vu une notification de LeakCanary disant qu'il n'avait pas réussi à obtenir un accès en écriture ( plus d'infos ). Je n'ai jamais vu la demande d'autorisation. Ainsi, dans un cas (non représenté dans la vidéo), j'ai donné à mon application un accès en écriture en allant dans l'application Paramètres, en trouvant mon application (par opposition à l'application appelée "Leak" installée par LeakCanary) et en activant l'accès en écriture. Dans la vidéo, je n'avais pas besoin de le faire car j'avais donné la permission en répondant à la notification. Puis, lorsque j'utilise mon application, je vérifie périodiquement les nouvelles notifications (en glissant vers le bas). J'ai vu des messages tels que "XYZActivity avait une fuite de 217 Ko". J'ai tapoté dessus et cela m'a emmené dans l'application Leak. 

De plus, j'ai remarqué que Parfois, l'analyse pouvait prendre plusieurs minutes et afficher une notification de fuite de mémoire sur votre téléphone .

Comment vérifier un correctif de fuite de mémoire à l'aide de LeakCanary

Maintenant que j'ai corrigé certaines de mes fuites de mémoire, j'utilise LeakCanary pour vérifier le correctif. Cependant, si LeakCanary ne rapporte rien, je ne crois pas nécessairement que c'est parce que ma fuite est réparée. Il se pourrait que LeakCanary soit cassé.

Comment vérifier un correctif de fuite de mémoire à l'aide de LeakCanary (16 minutes 34 secondes)

Processus de vérification d’une fuite de mémoire avec LeakCanary: 1. Vérifiez que la fuite de mémoire existe en utilisant LeakCanary 2. Corrigez une fuite de mémoire et confirmez que LeakCanary ne signale aucune fuite 3. Annulez votre correctif et confirmez que LeakCanary rapporte à nouveau la fuite.

Étant donné que LeakCanary affiche très peu d'informations sur le statut lorsqu'il fonctionne, il est difficile de savoir s'il fait quoi que ce soit. Cela m'a amené à penser que j'avais corrigé une fuite de mémoire alors que ce n'était pas le cas. Les trois étapes ci-dessus sont le meilleur moyen que j'ai trouvé de vérifier un correctif de fuite de mémoire à l'aide de LeakCanary.

1
Michael Osofsky

J'ai utilisé Leak-Canary comme ci-dessous:

1) Dépendance de Gradle:

debugImplementation 'com.squareup.leakcanary:leakcanary-Android:1.4'

2) classe d'application:

   @Override
public void onCreate() {
    super.onCreate();
    if (BuildConfig.DEBUG) {
        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                .detectCustomSlowCalls()
                .detectNetwork()
                .penaltyLog()
                .penaltyDeath()
                .build());
        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                .detectActivityLeaks()
                .detectLeakedClosableObjects()
                .detectLeakedRegistrationObjects()
                .detectLeakedSqlLiteObjects()
                .penaltyLog()
                .penaltyDeath()
                .build());

        LeakLoggerService.setupLeakCanary(this);
    }
}

3) classe LeakLoggerService: placez cette classe dans le paquet debug créé par gradle.

public class LeakLoggerService extends DisplayLeakService {

public static void setupLeakCanary(Application application) {
    if (LeakCanary.isInAnalyzerProcess(application)) {
        // This process is dedicated to LeakCanary for heap analysis.
        // You should not init your app in this process.
        return;
    }
    LeakCanary.install(application, LeakLoggerService.class,
            AndroidExcludedRefs.createAppDefaults().build());

}

@Override
protected void afterDefaultHandling(HeapDump heapDump, AnalysisResult result, String leakInfo) {
    if (!result.leakFound || result.excludedLeak) {
        return;
    }

    Log.w("LeakCanary", leakInfo);
}

4) Enregistrer le service dans le fichier manifeste et 1 permission:

  <uses-permission Android:name="Android.permission.WRITE_EXTERNAL_STORAGE"/>
  <service Android:name=".LeakLoggerService" />

5) Enfin, vérifiez si la configuration est réussie: Fuite d’une activité;)

public class Main2Activity extends AppCompatActivity {
static TextView label;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    label = new TextView(this);
    label.setText("asds");

    setContentView(label);
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 
}
}

tuer cette activité et vérifier les journaux avec tag: LeakCanary

Ça devrait marcher ...

0
100RaBH