Memory leaks in kotlin using leakcanary

Malcolm Maima
4 min readFeb 24, 2022

LeakCanary is a leak detection library for Android. It helps you detect memory leaks in Android apps.
I believe a good practice in any kind of software development is to always keep a close eye on memory leaks. This goes a long way in optimizing your app’s performance.
You’ve probably noticed in the past how your phone heats up when you open certain apps. This is because of the amount of memory used by the app. Optimizing this to an optimal amount of memory is a very important part of your app’s performance.

The occurrence of memory leaks is a very common problem in Android apps. It’s not uncommon for apps to have memory leaks, but it’s not always easy to find them. With LeakCanary, you can easily find and fix memory leaks in your Android apps and we’ll see how below.

Prerequisites

  • Android Studio
  • Basic Understanding of Kotlin

Memory Leaks

Whenever an object is created, it is stored in the heap. When an object is no longer needed, it is garbage collected. The garbage collector is responsible for removing it from the heap junk. If you don’t release the memory, it will eventually be collected and you’ll see a memory leak. You don’t want memory leaks in your Android apps :-)

A memory leak can also occur when you use the wrong type of object. For example, if you create a string object and then use it as an integer, you’ll see a memory leak. When your end user’s experience is constant app crashes then you’re bound to experience some negative reviews. The net effect of this is that your app will be less popular or have a negative reputation.
No matter the cause , when a memory leak occurs the Garbage Collector thinks that the object is still in use and will not remove it from the heap. But those objects or references should be released.

To avoid memory leaks, you should always avoid saving context, views, and other objects in the heap or background threads. In cases where you do not have control over the lifecycle of the objects, you should always use weak references. What do we mean by this? Well, when you create a weak reference, you’re saying that you don’t want to keep a strong reference to the object. If the object is no longer needed, the garbage collector will remove it from the heap.
examples are using static inner classes with a static reference to the outer class. Remember to always unregister broadcast receivers when you’re done with them. A good practice is to register them in the onResume method and unregister them in the onPause method and then you’re done. Preference to LiveData over model classes.

LeakCanary handles memory leaks in a number steps:

  • [ ] Finds memory leaks by detecting leaks that have been retained
  • [ ] Creates a report by dumping the heap to a file
  • [ ] Shows a notification
  • [ ] Creates a crash report
  • [ ] Creates a bug report by sorting the leaks into categories

Getting started

First thing we want to do is to add LeakCanary to our app’s build.gradle file.

dependencies {
implementation 'com.squareup.leakcanary:leakcanary-android:2.0'
}

The assumption is that you have an existing project that you want to add LeakCanary to. For the purposes of demonstration, we’ll be using the hello-world app. Feel free to fork it and use it as a template. Click here
in The MainActivity.kt file, we’ll add the following code:

class MainActivity : AppCompatActivity() {

private lateinit var binding: ActivityMainBinding
private var test: Int = 0

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

binding.btn.setOnClickListener {
startAsyncWork()
}
}

private fun startAsyncWork() {
test++
Thread(Runnable {
SystemClock.sleep(3000)
runOnUiThread {
binding.btn.text = "Leak: $test"
startAsyncWork()
}
}).start()
}
}

Did you notice anything with the above code? You’ll notice that startAsyncWork() is called recursively. On clicking the button, we initialize a new thread and update test variable after 3 seconds. This recursive thread will continue to run until the app is closed.
Without good control checks, this can cause a memory leak. We can avoid memory leaks in this scenario by setting a limit on the number of times we can call startAsyncWork().
To do this we can edit the above code as follows:

class MainActivity : AppCompatActivity() {

private lateinit var binding: ActivityMainBinding
private var test: Int = 0

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

binding.btn.setOnClickListener {
binding.btn.text = "Thread: $test"
startAsyncWork()
}
}

private fun startAsyncWork() {
test++
if (test < 10) {
Thread(Runnable {
SystemClock.sleep(3000)
runOnUiThread {
binding.btn.text = "Thread: $test"
startAsyncWork()
}
}).start()
} else {
Thread.currentThread().interrupt()
binding.btn.text = "Threads stopped at: $test"
test = 0
}
}
}

LeakCanary gives you a visual representation of the memory leaks. You can click on the leak to see the stack trace.

This comes in very handy when you’re trying to figure out where the leak is coming from and gives you first hand experience of the memory leak.
As you continue to interact with the app you’ll notice the notification that appears on the status bar.

This is a great way to see how LeakCanary is working. When Dumping the heap to a file, LeakCanary will create a file in the app’s cache directory called LeakCanary-hprof.txt.
You can open this file in a text editor and you’ll see the stack trace of the memory leak. Dumping of the heap to a file is a very useful feature for debugging and only happens when the threshold is met.

After heap dumping, you’ll notice leak canary will start analyzing the heap. It will create a report and show a notification.
Something to remember before pushing your app to production is that you need remove leak canary from your app’s build.gradle file. You can simply comment out the implementation line and you’re good to go.

References:

--

--

Malcolm Maima

Android Engineer @ Kyosk — I code stuff… lots of stuff (^_^) I eat a lot too, coffee makes me sleepy Love cartoons and robots. http://linktr.ee/malcolmmaima