Fix Memory Leak Android  -  View Binding / Data Binding

Fix Memory Leak Android - View Binding / Data Binding

Abhishek Kumar
·Jun 17, 2022·

2 min read

Subscribe to my newsletter and never miss my upcoming articles

Play this article

Table of contents

  • The problem
  • Alternate Solution

The problem

When working with JVM (Java Virtual Machine), the Garbage Collector will take care of allocating and deallocating memory for us - Most of the time. In some cases, it fails to release the memory for some objects which are no longer in use, this is called Memory Leak.

Memory leaks can lead to OutOfMemoryError exceptions and lags in the app hence it's essential to free memory up.

While there can be many reasons for memory leaks, one of the most common is caused when using View binding or Data binding in Fragments. This happens because the binding reference is kept in the memory even after the Fragment is destroyed.

What Google recommends -

Google recommends in its docs to

  • Make binding of type nullable
  • Set the binding reference to null on onDestroyView method of the Fragment
private var _binding: ResultProfileBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!


override fun onDestroyView() {
    super.onDestroyView()
    _binding = null
}

If you are working on a large project will a lot of fragments, this can be a tedious work to do instead we can use the alternate solution.

Alternate Solution

Add the following lifecycle dependency in build.gradle -

implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"

and create a Kotlin function that will automatically destroy binding object on onDestroy by observing the lifecycle owner -

fun <T> Fragment.viewLifecycleNullable(): ReadWriteProperty<Fragment, T> =
        object : ReadWriteProperty<Fragment, T>, DefaultLifecycleObserver {

            private var binding: T? = null
            private var viewLifecycleOwner: LifecycleOwner? = null

            init {
                 this@viewLifecycleNullable.viewLifecycleOwnerLiveData.observe(this@viewLifecycleNullable, { lifecycleOwner ->
                    viewLifecycleOwner?.lifecycle?.removeObserver(this)
                    viewLifecycleOwner = lifecycleOwner.also {
                        it.lifecycle.addObserver(this)
                    }
                })
            }

            override fun onDestroy(owner: LifecycleOwner) {
                super.onDestroy(owner)
                binding = null
            }

            override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
                return this.binding!!
            }

            override fun setValue(thisRef: Fragment, property: KProperty<*>, value: T) {
                this.binding = value
            }
        }

Now simply use it in Fragments like -

class ExampleFragment : Fragment(){
    private var binding: FragmentExampleBinding by viewLifecycleNullable()
}

Did you find this article valuable?

Support Abhishek Kumar by becoming a sponsor. Any amount is appreciated!

Learn more about Hashnode Sponsors
 
Share this