Stop collecting Flow in ViewModel when app in background

Issue

Need to collect flow in ViewModel and after some data modification, the UI is updated using _batteryProfileState.

Inside compose I’m collecting states like this

val batteryProfile by viewModel.batteryProfileState.collectAsStateWithLifecycle()
 batteryProfile.voltage

In ViewModel:

private val _batteryProfileState = MutableStateFlow(BatteryProfileState())
val batteryProfileState = _batteryProfileState.asStateFlow()

private fun getBatteryProfileData() {
    viewModelScope.launch {

          // FIXME In viewModel we should not collect it like this
         _batteryProfile(Unit).collect { result ->

               _batteryProfileState.update { state ->
                   when(result) {
                       is Result.Success -> {
                           state.copy(
                               voltage = result.data.voltage?.toString()
                                ?.plus(result.data.voltageUnit
                         )
                     }
                      is Result.Error -> {
                           state.copy(
                              errorMessage = _context.getString(R.string.something_went_wrong)
                         )
                     }
                }
             }
        }
     }
}

The problem is when I put my app in the background the _batteryProfile(Unit).collect does not stop collecting while in UI batteryProfile.voltage stop updating UI which is correct behavior as I have used collectAsStateWithLifecycle() for UI.
But I have no idea how to achieve the same behavior for ViewModel.

Solution

In ViewModel I have used stateIn operator and access data like below everything working fine now:

 val batteryProfileState = _batteryProfile(Unit).map { result ->
        when(result) {
            is Result.Success -> {
                BatteryProfileState(
                    voltage = result.data.voltage?.toString()
                        ?.plus(result.data.voltageUnit.unit)
                        ?: _context.getString(R.string.msg_unknown),
                )
            }
            is Result.Error -> {
                BatteryProfileState(
                    errorMessage = _context.getString(R.string.something_went_wrong)
                )
            }
        }
    }.stateIn(viewModelScope, WhileViewSubscribed, BatteryProfileState())

collecting data in composing will be the same
Explanation: WhileViewSubscribed Stops updating data while the app is in the background for more than 5 seconds.

val WhileViewSubscribed = SharingStarted.WhileSubscribed(5000)

Answered By – Lokik Soni

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published