How to use Leak Canary

Charas picture Charas · Nov 11, 2015 · Viewed 37.9k times · Source

I know this is probably a dumb question, but I am pretty new at developing android, and I currently experiencing an OutOfMemoryError in my apps, which I have tried to debug using MAT, but it is still too hard to find the leak in a few activities, then I found LeakCanary, which seems simpler and easier to use, however I could not find any beginner step by step guide on using Leak Canary, even on Google. I have installed LeakCanary through the dependencies in my build.gradle, and this is what I got so far :

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()

    }
}

Let's say I have an Activity where I want LeakCanary to watch an object

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 ?

    }
}

Now how do I use LeakCanary to see which object is causing the memory leak. Thank you for your help and assistance.

Answer

Budius picture Budius · Nov 11, 2015

The nice thing about leak canary is how automated it works. By default, it already "watches" for activities that are not being properly GCed. So out of the box, if any activity is leaking you should receive the notification.

On my project I've added an extra method on the Application like this:

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

so the important stuff with garbage collection and memory leak and canary is to know when stuff should be collected and ask that item to be watched.

For for example we're using a "base fragment" with the following code:

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

this way LeakCanary is trying to check if any fragment is leaking memory.

So for you to further implement on your app, you could/should on tasks or instances that you know it should be garbage collected but you think it might not be, and you're not sure where, you can call that too: ExampleApplication.instance.mustDie(object);

and then you MUST run the application and rotate the device and force the leak to happen, so leak canary can grab/analyse the stack trace and give you valuable information on how to fix it.

I hope it helps.