How To Avoid ANR & Memory Leaks In Android
- June 14, 2019
- Adnan Sattar
Building an Android app is easy, but making a super-high-quality, memory-efficient Android app is not. Many times we see ANR dialog while using android apps, a developers need to take many things into consideration to deliver state of the art solution, with purpose to make sure that apps are robust and do not crush.
A common cause of ANR is memory leaks. In most cases a steady increase in in memory usage happen until the app cannot allocate more resources. In Java this often results in an OutOfMemoryException being thrown. In some rare cases, leaked classes can even stick around for long enough to receive registered callbacks, causing some really strange bugs and all too often throw IllegalStateException.
In this article, I would focus on memory optimization techniques;
A memory leak happens when memory is allocated but never gets freed. A garbage collector (GC) runs periodically to check objects that are not in use and removes them. A memory leak will happen when there are objects that there are not in use by the app but the garbage collector cannot recognize them. Hence, they will remain in memory unused, reducing the amount of memory available for the app, which causes unexpected results.
Every object has got its own lifetime, after which it needs to say goodbye and leave the memory. But if some other object(s) is holding onto this object (directly or indirectly), then the garbage collector will not be able to collect it. Holding reference of objects that are not required anymore is a bad practice, freeing objects reference after being served is helpful for the garbage collector to kill that object.
There is an awesome library LeakCanary made by Square and it’s a very fast way to detect memory leaks. It is best for finding leaks in the mobile app along with the stack trace. Leak Canary allows you to detect memory leaks in longer runs because you don’t need to connect your device to the Android Studio and monitor your app for a long period of time. Leak Canary will send you notifications whenever there is a memory leak.
1. Static activity and view reference:
When we define any static reference to view or any class then it doesn’t collect by the garbage collector. Imagine your maid collects trash which only in a trash can, and you throw trash around the house. So your maid doesn’t collect that and it takes space in your house. This same situation happens in a mobile app.
How do we solve this?
Do not use static reference for views and any class. If somehow you need to use static then make it null after its work done.
2. AsyncTask Reference:
You are using an asyncTask to get a string value which is used to update the textView in OnPostExecute(). When we create AsynchTask It resides in memory until we don’t make it cancel, even if an activity is destroyed. Since its high priority thread garbage collector can’t take it away.
How Do we solve this?
- 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.
- 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.
3. Broadcast receiver:
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. Solution to this is quite simple, but you have to remember each time you write Broadcast receiver always call unregisterReceiver in onStop() or onPause().
4. Screen rotation:
When device rotation implies it creates a new activity with requested rotation and discards old activity. So, if any method or reference which refer to the old activity then its become leak, because it can not be freed from memory.
These bitmap objects are generally quite heavy, and if they are dealt with improperly, they can lead to significant memory leaks that eventually crash your app due to OutOfMemoryError. The bitmap memory related to image resources that you use in your app is automatically managed by the framework, but if you are dealing with bitmaps manually, be sure to recycle() them after use.
You should load large bitmaps by scaling them down, and use bitmap caching and pooling whenever possible to reduce memory usage. Here is a good resource to understand bitmap handling in depth.
Another common reason for memory leaks is the misuse of the Context instances. The Context is simply an abstract class. There are many classes (like Activity, Application, Service, etc.) that extend Context instances to provide their own functionalities.
But there is a difference between these Contexts. It is very important to understand the difference between the activity-level Context and the application-level Context and which one should be used under what circumstances.
Using the activity Context in the wrong place can keep a reference to the entire activity and cause a potential memory leak. Here is an excellent article for you to find out exactly which Context to use.
- Always make sure to unregister broadcast Receivers or timers inside the activity.
- Cancel any asyncTasks or Threads inside onDestroy().
- Always use a weakReference of the activity or view when needed.
- Never use static variables to declare views or activity context.
- 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.
- 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.
For more on, what is an ANR? What causes them and how can they be avoided? Among other concepts, check out this Android Guide.