9 ways to avoid memory leaks in Android

Anitaa Murthy
AndroidPub
Published in
6 min readMay 16, 2018

--

I have been an android developer for quite some time now. And I realised that most of that time, I tend to spend on adding new features to an app or working on visually enhancements of the app, rather than focusing on the core issues like performance and quality.

This was me a while back when I would be asked to optimise or refactor code

But these things have a way of catching up to you and I spent a long time wishing I had not ignored the important stuff. So in this article, I thought I would focus on one of the most important optimisation techniques in android: Memory leaks.

There are a lot of articles on memory leaks and how to fix them. But when I was learning myself, I found that none of them were in one place and it was hard to keep track of it all. So I thought I would collectively post an article on it so that it might help people in the future.

So let’s begin.

What are memory leaks?

A memory leak happens when memory is allocated but never freed. This means the garbage collector is not able to take out the trash once we are done with the takeout. Initially, this might not be a problem. But imagine if you don’t take the trash out for 2 weeks! The house starts to smell right?

Similarly, as the user keeps on using our app, the memory also keeps on increasing and if the memory leaks are not plugged, then the unused memory cannot be freed up by the Garbage Collection. So the memory of our app will constantly increase until no more memory can be allocated to our app, leading to OutOfMemoryError which ultimately crashes the app.

So how do I check if my app is leaking?

I am sure everyone on the planet is aware of it but just in case, there is an awesome library called LeakyCanary that is great for finding out the leaks in our app along with the stack trace.

So what are some of the common mistakes that lead to memory leaks?

1. Broadcast Receivers:

Consider this scenario - you need to register a local broadcast receiver in your activity. If you don’t unregister the broadcast receiver, then it still holds a reference to the activity, even if you close the activity.

How to solve this? Always remember to call unregister receiver in onStop() of the activity.

Note: As Artem Demyanov pointed out, if the broadcast Receiver is registered in onCreate(), then when the app goes into the background and resumed again, the receiver will not be registered again. So it is always good to register the broadcastReceiver in onStart() or onResume() of the activity and unregister in onStop().

2. Static Activity or View Reference:

Consider the below example — You are declaring a TextView as static (for whatever reason). If you reference an activity or view directly or indirectly from a static reference, the activity would not be garbage collected after it is destroyed.

How to solve this? Always remember to NEVER use static variables for views or activities or contexts.

3. Singleton Class Reference:

Consider the below example — you have defined a Singleton class as displayed below and you need to pass the context in order to fetch some files from the local storage.

The SingletonSampleClass. java

This was actually an example I had to use in an app of mine: for REST services.

How to solve this?
Option 1:
Instead of passing activity context i.e. this to the singleton class, you can pass applicationContext().
Option 2: If you really have to use activity context, then when the activity is destroyed, ensure that the context you passed to the singleton class is set to null.

4. Inner Class Reference:

Consider the below example — you have defined a inner class called LeakyClass.java as displayed below and you need to pass the activity in order to redirect to a new activity.

How to solve this?
Option 1:
As mentioned before never create a static variable of an inner class.
Option 2: The class should be set to static. Instances of anonymous classes do not hold an implicit reference to their outer class when they are “static”.
Option 3: Use a weakReference of any view/activity. Garbage collector can collect an object if only weak references are pointing towards it.

5. Anonymous Class Reference:

This follows the same theory as above. Sample implementation for fixing memory leak is given below:

6. AsyncTask Reference:

Consider the following example — You are using an asyncTask to get a string value which is used to update the textView in OnPostExecute().

How to solve this?
Option 1:
We should always cancel the asyncTask when activity is destroyed. This is because the asyncTask will still be executing even if the activity is destroyed.
Option 2: NEVER reference a class inside the activity. If we definitely need to, we should set the class as static as static inner classes don’t hold any implicit reference to its parent activity class.
Option 3: Use a weakReference of the textview.

7. Handler Reference:

Consider the following example — You are using a Handler to redirect to a new screen after 5 seconds.

How to solve this?
Option 1:
NEVER reference a class inside the activity. If we definitely need to, we should set the class as static. This is because when a Handler is instantiated on the main thread, it is associated with the Looper’s message queue. Messages posted to the message queue will hold a reference to the Handler so that the framework can call Handler#handleMessage(Message) when the Looper eventually processes the message.
Option 2: Use a weakReference of the Activity.

8. Threads Reference:

We can repeat this same mistake again with both the Thread and TimerTask classes.

How to solve this?
Option 1:
Non-static anonymous classes hold an implicit reference to their enclosing class.
Option 2: Close thread in activity onDestroy() to avoid thread leak.

9. TimerTask Reference:

The same principle is as Threads can be followed for TimerTask as well. Sample implementation for fixing memory leak is given below:

How to solve this?
Option 1:
Cancel timer in activity onDestroy() to avoid memory leak.

So basically to summarise:
1. Use applicationContext() instead of activity context when possible. If you really have to use activity context, then when the activity is destroyed, ensure that the context you passed to the class is set to null.
2. Never use static variables to declare views or activity context.
3. Never reference a class inside the activity. If we need to, we should declare it as static, whether it is a thread or a handler or a timer or an asyncTask.

4. Always make sure to unregister broadcastReceivers or timers inside the activity. Cancel any asyncTasks or Threads inside onDestroy().
5. Always use a weakReference of the activity or view when needed.

And that’s it folks! Thanks for reading! I hope you enjoyed this article and found it useful, if so please hit the Clap button. Let me know your thoughts in the comments section.

Happy coding!

--

--