Simplify and reduce overhead of lazy view binding in Fragments (#4269)
This reduces complexity of view binding inflation in Fragments, and also reduces overhead (no `KProperty` objects need to be generated by the compiler) by implementing `Lazy` instead of `ReadOnlyProperty`. For a full explanation, see this [detailed blog post](https://medium.com/@bladecoder/viewlifecyclelazy-and-other-ways-to-avoid-view-memory-leaks-in-android-fragments-4aa982e6e579).
This commit is contained in:
parent
7e5eef4060
commit
a19540f0e4
|
@ -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 <T : ViewBinding> AppCompatActivity.viewBinding(
|
||||
|
@ -22,55 +20,32 @@ inline fun <T : ViewBinding> AppCompatActivity.viewBinding(
|
|||
bindingInflater(layoutInflater)
|
||||
}
|
||||
|
||||
class FragmentViewBindingDelegate<T : ViewBinding>(
|
||||
val fragment: Fragment,
|
||||
val viewBindingFactory: (View) -> T
|
||||
) : ReadOnlyProperty<Fragment, T> {
|
||||
private var binding: T? = null
|
||||
private class ViewLifecycleLazy<out T : Any>(
|
||||
private val fragment: Fragment,
|
||||
private val initializer: (View) -> T
|
||||
) : Lazy<T>, LifecycleEventObserver {
|
||||
private var cached: T? = null
|
||||
|
||||
init {
|
||||
fragment.lifecycle.addObserver(object : DefaultLifecycleObserver {
|
||||
val viewLifecycleOwnerLiveDataObserver =
|
||||
Observer<LifecycleOwner?> {
|
||||
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 <T : ViewBinding> Fragment.viewBinding(viewBindingFactory: (View) -> T) =
|
||||
FragmentViewBindingDelegate(this, viewBindingFactory)
|
||||
fun <T : ViewBinding> Fragment.viewBinding(viewBindingFactory: (View) -> T): Lazy<T> =
|
||||
ViewLifecycleLazy(this, viewBindingFactory)
|
||||
|
|
Loading…
Reference in New Issue