refactor: Simplify and optimize viewBinding delegate (#544)
Based on Christophe Beyl's technique described in https://bladecoder.medium.com/viewlifecyclelazy-and-other-ways-to-avoid-view-memory-leaks-in-android-fragments-4aa982e6e579
This commit is contained in:
parent
c2fc3d1f08
commit
4f9b283432
|
@ -21,16 +21,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
|
||||
* https://bladecoder.medium.com/viewlifecyclelazy-and-other-ways-to-avoid-view-memory-leaks-in-android-fragments-4aa982e6e579
|
||||
* by Christophe Beyls
|
||||
*/
|
||||
|
||||
inline fun <T : ViewBinding> AppCompatActivity.viewBinding(
|
||||
|
@ -39,53 +37,32 @@ inline fun <T : ViewBinding> AppCompatActivity.viewBinding(
|
|||
bindingInflater(layoutInflater)
|
||||
}
|
||||
|
||||
class FragmentViewBindingDelegate<T : ViewBinding>(
|
||||
val fragment: Fragment,
|
||||
val viewBindingFactory: (View) -> T,
|
||||
) : ReadOnlyProperty<Fragment, T> {
|
||||
private class ViewLifecycleLazy<out T : Any>(
|
||||
private val fragment: Fragment,
|
||||
private val viewBindingFactory: (View) -> T,
|
||||
) : Lazy<T>, LifecycleEventObserver {
|
||||
private var binding: T? = null
|
||||
|
||||
init {
|
||||
fragment.lifecycle.addObserver(
|
||||
object : DefaultLifecycleObserver {
|
||||
val viewLifecycleOwnerLiveDataObserver =
|
||||
Observer<LifecycleOwner?> {
|
||||
val viewLifecycleOwner = it ?: return@Observer
|
||||
override val value: T
|
||||
get() {
|
||||
return binding ?: run {
|
||||
val newValue = viewBindingFactory(fragment.requireView())
|
||||
binding = newValue
|
||||
fragment.viewLifecycleOwner.lifecycle.addObserver(this)
|
||||
newValue
|
||||
}
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycle.addObserver(
|
||||
object : DefaultLifecycleObserver {
|
||||
override fun onDestroy(owner: LifecycleOwner) {
|
||||
override fun isInitialized() = binding != null
|
||||
|
||||
override fun toString() = binding.toString()
|
||||
|
||||
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
|
||||
if (event == Lifecycle.Event.ON_DESTROY) {
|
||||
binding = null
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCreate(owner: LifecycleOwner) {
|
||||
fragment.viewLifecycleOwnerLiveData.observeForever(viewLifecycleOwnerLiveDataObserver)
|
||||
}
|
||||
|
||||
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.")
|
||||
}
|
||||
|
||||
return viewBindingFactory(thisRef.requireView()).also { this.binding = it }
|
||||
}
|
||||
}
|
||||
|
||||
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