Some fragment observers trigger after pop from back stack although data is not changed

后端 未结 2 817
生来不讨喜
生来不讨喜 2020-12-11 18:30

I have some problem in nested fragment in Kotlin. I have nested fragment with ViewModel. After resuming fragment from back button press all observers on viewModel LiveData t

相关标签:
2条回答
  • 2020-12-11 19:16

    LiveData always stores the last value and sends it to each Observer that is registered. That way all Observers have the latest state.

    As you're using viewLifecycleOwner, your previous Observer has been destroyed, so registering a new Observer is absolutely the correct thing to do - you need the new Observer and its existing state to populate the new views that are created after you go back to the Fragment (since the original Views are destroyed when the Fragment is put on the back stack).

    If you're attempting to use LiveData for events (i.e., values that should only be processed once), LiveData isn't the best API for that as you must create an event wrapper or something similar to ensure that it is only processed once.

    0 讨论(0)
  • 2020-12-11 19:31

    After knowing what happen I decide to go with customized live data to trigger just once. ConsumableLiveData. So I will put answer here may help others.

    class ConsumableLiveData<T>(var consume: Boolean = false) : MutableLiveData<T>() {
    
        private val pending = AtomicBoolean(false)
    
        override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
            super.observe(
                owner,
                Observer<T> {
                    if (consume) {
                        if (pending.compareAndSet(true, false)) observer.onChanged(it)
                    } else {
                        observer.onChanged(it)
                    }
                }
            )
        }
    
        override fun setValue(value: T) {
            pending.set(true)
            super.setValue(value)
        }
    }
    

    And for usage just put as bellow. It will trigger just once after any update value. This will great to handle navigation or listen to click or any interaction from user. Because just trigger once!

    //In viewModel
    val goToCreditCardLiveData = ConsumableLiveData<Boolean>(true)
    

    And in fragment:

    viewModel.goToCreditCardLiveData.observe(viewLifecycleOwner) {
            findNavController().navigate(...)
    
        }
    
    0 讨论(0)
提交回复
热议问题