Home > Software design >  Stop collecting Flow in ViewModel when app in background
Stop collecting Flow in ViewModel when app in background

Time:08-25

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

Inside compose I am 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.
Note I need to collect this data in method getBatteryProfileData() because this function needs to call from two places i.e initially inside init of ViewModel and from refresh

CodePudding user response:

try adding a variable to your ViewModel that tracks the activity state, and update it onResume() and onPause(). Then in your getBatteryProfileData() function, check that variable (stop and restart the Flow depending on what you see).

So in your activity:

val inForeground = false

onResume(){
    ....
    inForeground = true
}

onPause(){
    ....
    inForeground = false
}

CodePudding user response:

You can try to define getBatteryProfileData() as suspend fun:

suspend fun getBatteryProfileData() {
       

            // 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)
                            )
                        }
                    }
                }
            }
        
    }

And than in your composable define scope:

scope = rememberCoroutineScope()
            scope.launch {
               yourviewmodel.getBatteryProfileData()
            }

And I think you can move suspend fun getBatteryProfileData() out of ViewModel class...

CodePudding user response:

You should use collectAsStateWithLifecycle(), check this blog post.

  • Related