Android Fragment Collect SharedFlow from Shared ViewModel Issue

1.3k views Asked by At

Currently I have a ViewPager2 Fragment (as the start destination), which holds two child Fragments: ActiveOrderFragment and CompletedOrderFragment, they both have their own ViewModels to handle the api calls (to get the active orders and completed orders).

When the user clicks on the order item it will navigate to OrderEditorFragment, and there is a "Set Item as Active/Completed" button, clicking on that will update the status of the order (just simply a PUT request), and go back to the ViewPager2 Fragment.

In order to notify & refresh ActiveOrderFragment and CompletedOrderFragment, I created a SharedFlow in OrderViewModel, which is a shared ViewModel:

class OrderViewModel(application: Application) : AndroidViewModel(application) {
    private val uiScope = viewModelScope
    private val _isOrderStatusChanged = MutableSharedFlow<Boolean>(replay = 1)          

    val isOrderStatusChanged: SharedFlow<Boolean>
        get() = _isOrderStatusChanged

    fun updateIsOrderStatusChanged() = _isOrderStatusChanged.emit(uiScope, true)            
}

Then I observe (collect) this SharedFlow in onCreateView() of ActiveOrderFragment and CompletedOrderFragment:

viewLifecycleOwner.lifecycleScope.launch {
    viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.CREATED) {
        orderViewModel.isOrderStatusChanged.collect { 
            if (it) {
                Log.v("xxx", "refreshed!")
                viewModel.refreshActiveOrders()    //similar in CompletedOrderFragment
            }
        }
    }
}

Here comes the issue: when I changed the status and go back to the pages, they both get notified and refreshed. However, now I click on any order item to go to the editor page but do nothing, and go back, the collect{} gets called again, which is not an expected behavior. How to prevent that? I guess that's relevant to the replay property...


Some extra info:

  1. Why use viewLifecycleOwner instead of lifecycleScope directly.
  2. Why need replay = 1 for SharedFlow.
1

There are 1 answers

1
CTD On
MutableSharedFlow<Boolean>(replay = 1) == MutableStateFlow<Boolean>(false)

what is means replay

MutableSharedFlow<Boolean>(replay = 0) catch nothing

MutableSharedFlow<Boolean>(replay = 1) catch latest value

MutableSharedFlow<Boolean>(replay = 2) catch latest 2 value

orderViewModel.isOrderStatusChanged.collect {
            Log.i("isOrderStatusChanged","$it")
        }

if you set _isOrderStatusChanged twice with diff value, you will find out.