I am currently experiencing an OutOfMemoryError in my apps. 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 functions // How do I use LeakCanary to watch these objects? } }
Now how do I use LeakCanary to see which object is causing the memory leak?
LeakCanary hooks into the Android lifecycle to automatically detect when activities and fragments are destroyed and should be garbage collected. These destroyed objects are passed to an ObjectWatcher , which holds weak references to them.
LeakCanary is a memory leak detection library for Android. LeakCanary's knowledge of the internals of the Android Framework gives it a unique ability to narrow down the cause of each leak, helping developers dramatically reduce Application Not Responding freezes and OutOfMemoryError crashes.
LeakCanary is an open-source memory leak detection library developed by Square organization.
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.
Posting this answer because all other answers are no longer needed and there's a better way to find leaks on your app using Leak Canary 2.
Just add below dependency to your gradle. No more code needed.
debugImplementation 'com.squareup.leakcananry:leakcanary-android:2.7'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With