diff --git a/app/src/main/java/com/keylesspalace/tusky/util/ViewBindingExtensions.kt b/app/src/main/java/com/keylesspalace/tusky/util/ViewBindingExtensions.kt index 86bca03f6..4ea3edeab 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/ViewBindingExtensions.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/ViewBindingExtensions.kt @@ -4,16 +4,14 @@ import android.view.LayoutInflater import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment -import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.Observer import androidx.viewbinding.ViewBinding -import kotlin.properties.ReadOnlyProperty -import kotlin.reflect.KProperty /** - * https://medium.com/@Zhuinden/simple-one-liner-viewbinding-in-fragments-and-activities-with-kotlin-961430c6c07c + * Original code: https://medium.com/@Zhuinden/simple-one-liner-viewbinding-in-fragments-and-activities-with-kotlin-961430c6c07c + * Refactor: https://bladecoder.medium.com/viewlifecyclelazy-and-other-ways-to-avoid-view-memory-leaks-in-android-fragments-4aa982e6e579 */ inline fun AppCompatActivity.viewBinding( @@ -22,55 +20,32 @@ inline fun AppCompatActivity.viewBinding( bindingInflater(layoutInflater) } -class FragmentViewBindingDelegate( - val fragment: Fragment, - val viewBindingFactory: (View) -> T -) : ReadOnlyProperty { - private var binding: T? = null +private class ViewLifecycleLazy( + private val fragment: Fragment, + private val initializer: (View) -> T +) : Lazy, LifecycleEventObserver { + private var cached: T? = null - init { - fragment.lifecycle.addObserver(object : DefaultLifecycleObserver { - val viewLifecycleOwnerLiveDataObserver = - Observer { - val viewLifecycleOwner = it ?: return@Observer - - viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver { - override fun onDestroy(owner: LifecycleOwner) { - binding = null - } - }) - } - - override fun onCreate(owner: LifecycleOwner) { - fragment.viewLifecycleOwnerLiveData.observeForever( - viewLifecycleOwnerLiveDataObserver - ) + override val value: T + get() { + return cached ?: run { + val newValue = initializer(fragment.requireView()) + cached = newValue + fragment.viewLifecycleOwner.lifecycle.addObserver(this) + newValue } - - override fun onDestroy(owner: LifecycleOwner) { - fragment.viewLifecycleOwnerLiveData.removeObserver( - viewLifecycleOwnerLiveDataObserver - ) - } - }) - } - - override fun getValue(thisRef: Fragment, property: KProperty<*>): T { - val binding = binding - if (binding != null) { - return binding } - val lifecycle = fragment.viewLifecycleOwner.lifecycle - if (!lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) { - throw IllegalStateException( - "Should not attempt to get bindings when Fragment views are destroyed." - ) - } + override fun isInitialized() = cached != null - return viewBindingFactory(thisRef.requireView()).also { this.binding = it } + override fun toString() = cached.toString() + + override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { + if (event == Lifecycle.Event.ON_DESTROY) { + cached = null + } } } -fun Fragment.viewBinding(viewBindingFactory: (View) -> T) = - FragmentViewBindingDelegate(this, viewBindingFactory) +fun Fragment.viewBinding(viewBindingFactory: (View) -> T): Lazy = + ViewLifecycleLazy(this, viewBindingFactory)