Memory leak may happen
If we reference viewBinding again in onDestroyView, this will trigger ViewBindingProperty#getValue, which will inflate another view if cache is null.
example here:
class PersonListFragment : Fragment(R.layout.fragment_person_list) {
// Without reflection
private val viewBinding by viewBinding(FragmentPersonListBinding::bind)
override fun onDestroyView() {
super.onDestroyView()
// If we reference viewBinding again in onDestroyView, this will trigger ViewBindingProperty#getValue, which will inflate another view if cached is null
viewBinding.xxx
}
inner class VBFragmentLifecycleCallback(
fragment: Fragment,
) : FragmentManager.FragmentLifecycleCallbacks() {
private val fragment by weakReference(fragment)
override fun onFragmentViewDestroyed(
fm: FragmentManager,
f: Fragment,
) {
// clear cache
if (fragment === f) clear()
}
}
}
public open class LazyViewBindingProperty<in R : Any, T : ViewBinding>(
private val viewBinder: (R) -> T,
) : ViewBindingProperty<R, T> {
private var viewBinding: T? = null
public override fun getValue(
thisRef: R,
property: KProperty<*>,
): T {
return viewBinding ?: viewBinder(thisRef).also { viewBinding = it }
}
@CallSuper
public override fun clear() {
viewBinding = null
}
}
Hi. Yes, it is possible. I tried to find an expected behavior. In my opinion, it must throw an exception to prevent usage after super.onDestroyView(). Do you have any ideas?
Hi. Yes, it is possible. I tried to find an expected behavior. In my opinion, it must throw an exception to prevent usage after
super.onDestroyView(). Do you have any ideas?
We should give callers an opportunity to access the ViewBinding in onDestroyView(). Consider a common scenario: when a Fragment's layout contains a WebView, we need to call WebView.destroy() to release resources and prevent memory leaks. This cleanup is typically performed in onDestroyView().
To support this use case, we can delay the ViewBinding cleanup until after onDestroyView() completes. For example:
override fun onFragmentViewDestroyed(
fm: FragmentManager,
f: Fragment,
) {
if (fragment === f) {
// make sure to let onDestroyView() run first
Handler().post {
// clear cache
clear()
}
// coroutine version
ProcessLifecycleOwner.get().lifecycleScope.launch {
// make sure to let onDestroyView() run first
yield()
clear()
}
}
}
When Fragment instance will be collected and after this VB instance will be collected too. Memory leak possible in case when developer keep reference outside Fragmnet