'Why not use lateinit modifier in Andrioid Fragment view-binding?

In android documentation we have example of view binding without lateinit:

private var _binding: ResultProfileBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    _binding = ResultProfileBinding.inflate(inflater, container, false)
    val view = binding.root
    return view
}

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

Why we are not using lateinit, like we use it in activity:

private lateinit var binding: ResultProfileBinding? = null

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    binding = ResultProfileBinding.inflate(inflater, container, false)
    return binding.root
}

I suspect it has something with a memory leak problem. Can you explain it?



Solution 1:[1]

I found a good explanation here.

Snippet from the explanation:

How do leaks happen in fragments? First, we need to start by reviewing the important nuance of fragments. They have two different lifecycles:

  • It’s own lifecycle (onCreate and onDestroy)
  • It’s view’s lifecycle (onCreateView and onDestroyView)

Having two lifecycles for a single screen can be problematic. They are created and destroyed at different times, for instance, when putting a fragment on the back-stack. Specifically, holding onto views after onDestroyView is called will leak. This happens when a fragment is on the back stack, and although its view is destroyed, the fragment itself is not. The garbage collector is unable to clear the reference to those views.

And one snippet from this Stack Overflow answer:

You have to null out your references to the views in onDestroyView as that's the sign that the view is no longer being used by the Fragment system and it can be safely garbage collected if it wasn't for your continued reference to the View.

Solution 2:[2]

Take a look at this example

// here a firestore database uses callback to be executed when the document recieved 
db.collection("cities").document("SF").get()
        .addOnSuccessListener { document ->
            if (document != null) {
                binding.textView.text = document.data.toString()
            } else {
                Log.d(TAG, "No such document")
            }
        }

if the user opened the fragment and closed it before the document received (that's mean the fragment no longer be used and should clear all it's variables by garbage collector if the variable is null or no longer used)

Now let's discuss what the scenario with lateinit

private lateinit var binding: ResultProfileBinding

the garage collector will not clear the binding as it still used in the callback and the fragment will remain in the memory which lead a memmory leak, then however the callback executed and set the text the user won't know about that because he left the fragment

Imagine if the user did this scenario multiple times !!

So what about nullable binding ?

private var _binding: ResultProfileBinding? = null
private val binding get() = _binding!!

you setting it to null in onDestroyView so the binding and fragment can be garbage collected (No Memory leaks)

BUT is what will happen when the callback executed?

you will get a NullPointerException so be aware of that

and whatever how many times the user open the fragment and closed it it will garbage collected

give yourself a try with this code, you can use Android Studio Profiler to watch device memory and/or leakCanary to get notify about the app memory leaks

Solution 3:[3]

binding in fragments could lead to memory leaks, if they aren't set to null in onDestroyView. That's why we set _binding to null in onDestroyView

While on using lateinit, we cannot assign a lateinit property to null. So, if we use lateinit binding we won't be able to set it to null in onDestroyView and it would lead to memory leak. It is why android documentation recommends using a nullable variable.

In case of activities we don't need to assign binding to null as they don't lead to leak memory, and we can use a lateinit property for binding.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 matej-m
Solution 2 Hussien Fahmy
Solution 3