From ffc583d0e5cc762b718efb0336aac870ff425629 Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli <mohamed.metwalli@sap.com> Date: Thu, 10 Jun 2021 19:26:58 +0200 Subject: [PATCH] Reset viewBing when new view is created (#3409) Co-authored-by: harambasicluka <64483219+harambasicluka@users.noreply.github.com> --- .../util/ui/ViewBindingExtensions.kt | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ui/ViewBindingExtensions.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ui/ViewBindingExtensions.kt index 21b663e70..279dc35a4 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ui/ViewBindingExtensions.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ui/ViewBindingExtensions.kt @@ -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" -- GitLab