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
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 field
binding. 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
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
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?
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
Create a class
AutoCleanedValue.kt where we’ll wrap our logic for auto-nullifying references 👇.
Let’s understand it
AutoCleanedValueis implementing a
ReadWritePropertywhich 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
initializerlambda (Optional) which provides the initial value which might be helpful for immutable types.
- There’s a field
_valuewhich 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
nulldue 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! 😄