Let your delegates auto-nullify references☠️

Let your delegates auto-nullify references☠️

In this article, we’ll see how to auto-clear memory references with Kotlin’s delegated properties to avoid memory leaks in your Android app

Hello Android developers 👋, In this article, we'll explore Kotlin's Delegated property feature to solve a common problem in Android which will help us to reduce memory leaks.

Memory management is an important thing to take into consideration while developing a good and performant application. While developing any application, a memory leak is a common problem while developing an application if we didn't handle resources properly as per the lifecycle events.

Let me give you examples. Imagine if you have a Fragment and it has references to some properties. We need to clear the references to these fields by assigning null in onDestroyView() lifecycle method so that Fragment won't hold references to them.


🎬 Example 1

You may have seen this snippet of code on official Android's docs for ViewBinding 👇

Here, you can see that we need to create two fields _binding which is a nullable backing field for non-null fieldbinding. What’s the reason for this? Read this 👇

Note: As this is mentioned in the official docs. Fragments outlive their views. Make sure you clean up any references to the binding class instance in the fragment’s onDestroyView() method.

In this example, we need to declare these things twice. If we forgot to clear reference in onDestroyView() then it might lead to issues. We’ll see how to solve this after some time. Till then let’s take a look at another example.

🎬 Example 2

When you have worked with RecyclerView, sometime you might have experienced a memory leak issue due to RecyclerView.Adapter even in configuration changes like a rotating screen. So for such cases, again we need to clean adapter reference in onDestroyView() lifecycle method as following 👇.

Here you can see that we finally setting RecyclerView’s Adapter as null. Alternatively, as mentioned in OR (2️⃣) comments, we can also declare mAdapter as nullable. But if we do so then we'll need to use ? with adapter instance every time we try to access it 😫.

Here again, if we forget to set adapter as null in onDestroyView() then it might be a problem if there’s any process that tries to access the adapter.

In both examples, we used backing fields for references like _ref for every ref and nullified it in onDestroyView(). So how to avoid this repetition, make it more readable or reduce boilerplate for every Fragment/Activity or Android’s component?


💡 Solution

The Jetpack library for Lifecycle is a very powerful library by which we can develop Android lifecycle-aware components which allow us to observe the lifecycle of components and thus we can handle things accordingly. Also, Kotlin has a variety of features and the Delegated property is one of the great features. So let’s see it in action.

I read the source code of AutoClearedValue from official samples and Gabor Varadi’s article where he mentioned a bug in this implementation. So this solution is inspired by the learnings of these two references.

First of all, add these Jetpack Lifecycle Component libraries

implementation("androidx.lifecycle:lifecycle-common-java8:2.3.0")  
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.3.0")

Create a class AutoCleanedValue.kt where we’ll wrap our logic for auto-nullifying references 👇.

Let’s understand it

  • AutoCleanedValue is implementing a ReadWriteProperty which is a base interface from the standard library that can be used for implementing property delegates of read-write properties.
  • It has two parameters in the constructor i.e. fragment (since we are making it for Fragment as of now) and initializer lambda (Optional) which provides the initial value which might be helpful for immutable types.
  • There’s a field _value which will act as a backing field for references.
  • On initialization, we are observing the View lifecycle of a fragment and nullifying it when the view lifecycle is destroyed.
  • In short, anybody can’t access the reference once Fragment’s onDestoryView() lifecycle method is called.
  • If the value is retrieved and if the current value is null due to Fragment's lifecycle then its value will be re-initialized with the help of initializer (if it's provided).

Okay! This is good. Now to make it easy to access from Fragments, let’s make an extension function?

Cool! How we are going to use it? Looks like everything is settled. Let’s see in action how our Fragment will look like after using this delegate? Let’s summarize both the examples we saw in this article in a single snippet 👇.

That’s it. Looking cool😍, isn’t it?

What we achieved?

  • We don't need to have an extra nullable backing field means we don't need to have two fields for the same reference which will reduce the repetition of fields.
  • No need to nullify it manually in onDestroyView() since the delegate will take care of it and thus we now can reuse this for every Fragment in a project.
  • Thus it's now more readable with a reduced boilerplate 😎.

Now, this was just for Fragment. If we need we can extend it up to Activity, Service, etc.

I hope you liked this article. Share this if you find it helpful so that it can help someone who needs it. Sharing is caring!

Thank you. Have fun! 😄


📚 References:

Did you find this article valuable?

Support Shreyas Patil by becoming a sponsor. Any amount is appreciated!