Skip to content
Snippets Groups Projects
Unverified Commit ffc583d0 authored by Mohamed Metwalli's avatar Mohamed Metwalli Committed by GitHub
Browse files

Reset viewBing when new view is created (#3409)

parent fc844e3a
No related branches found
No related tags found
No related merge requests found
......@@ -12,6 +12,7 @@ import androidx.viewbinding.ViewBinding
import timber.log.Timber
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
import com.google.android.material.bottomnavigation.BottomNavigationView
inline fun <reified BindingT : ViewBinding> Fragment.viewBinding() =
viewBinding(
......@@ -44,12 +45,13 @@ class ViewBindingProperty<ComponentT : LifecycleOwner, BindingT : ViewBinding>(
private val onDestroyObserver = object : DefaultLifecycleObserver {
// Called right before Fragment.onDestroyView
override fun onDestroy(owner: LifecycleOwner) {
Timber.tag(TAG).v("onDestroy(%s)", owner)
localRef?.lifecycle?.removeObserver(this) ?: return
localRef = null
uiHandler.post {
Timber.v("Resetting viewBinding")
Timber.tag(TAG).v("Resetting viewBinding owner=%s", owner)
viewBinding = null
}
}
......@@ -58,15 +60,34 @@ class ViewBindingProperty<ComponentT : LifecycleOwner, BindingT : ViewBinding>(
@MainThread
override fun getValue(thisRef: ComponentT, property: KProperty<*>): BindingT {
if (localRef == null && viewBinding != null) {
Timber.w("Fragment.onDestroyView() was called, but the handler didn't execute our delayed reset.")
Timber.tag(TAG).w(
"Fragment.onDestroyView(%s) was called, but the handler didn't execute our delayed reset.",
thisRef
)
/**
* There is a fragment racecondition if you navigate to another fragment and quickly popBackStack().
* There is a fragment race condition if you navigate to another fragment and quickly popBackStack().
* Our uiHandler.post { } will not have executed for some reason.
* In that case we manually null the old viewBinding, to allow for clean recreation.
*/
viewBinding = null
}
/**
* This is a fix for an edge case where the fragment is created but was never visible to the user
* [DefaultLifecycleObserver.onDestroy] was never called despite that the [Fragment.onDestroyView] was called,
* therefore the ViewBinding will not be set to `null` and will hold the old view ,while Fragment will
* inflate a new [View] when navigating back to it. As result of that the screen ends being blank.
*
* This is very specific case when navigating by deeplink to one of the [BottomNavigationView] destinations,
* that is not the "home destination" of the graph.
*/
(localRef as? Fragment)?.view?.let {
if (it != viewBinding?.root && localRef === thisRef) {
Timber.tag(TAG).w("Different view for the same fragment, resetting old viewBinding")
viewBinding = null
}
}
viewBinding?.let {
// Only accessible from within the same component
require(localRef === thisRef)
......@@ -76,9 +97,12 @@ class ViewBindingProperty<ComponentT : LifecycleOwner, BindingT : ViewBinding>(
val lifecycle = lifecycleOwnerProvider(thisRef).lifecycle
return bindingProvider(thisRef).also {
Timber.tag(TAG).d("bindingProvider(%s)", thisRef)
viewBinding = it
localRef = thisRef
lifecycle.addObserver(onDestroyObserver)
}
}
}
private const val TAG = "ViewBindingExtension"
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment