Leak-Proofing Your Android App | Fix Memory Leak Android - View Binding / Data Binding
Table of contents
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()
}