ViewBindingPropertyDelegate icon indicating copy to clipboard operation
ViewBindingPropertyDelegate copied to clipboard

Memory leak may happen

Open initialjie opened this issue 5 months ago • 2 comments

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
    }
}

initialjie avatar Nov 13 '25 09:11 initialjie

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?

kirich1409 avatar Nov 13 '25 12:11 kirich1409

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()
        }
    }
}

initialjie avatar Nov 13 '25 13:11 initialjie

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

kirich1409 avatar Jan 17 '26 17:01 kirich1409