'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
andonDestroy
)- It’s view’s lifecycle (
onCreateView
andonDestroyView
)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 theView
.
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 |