I want to change variable value or call function according to lifecycle in jetpack compose. I tried according to this answer. Also read from the doc. I did this without any problem.
I have @Composable function
@Composable
fun BluetoothConnectionContentStateful(
context: Context = LocalContext.current,
viewModel: PairViewModel = getViewModel()
) {
var selectedIndexOfAvailableItem by remember { mutableStateOf(DEVICE_NOT_SELECTED_INDEX) }
val rememberPairScreenState = rememberConnectionScreenState(context, viewModel)
val bluetoothEnableState by remember { derivedStateOf { viewModel.isBluetoothEnabled } }
LaunchedEffect(key1 = bluetoothEnableState) {
viewModel.handleBluetoothScanState(rememberPairScreenState.bluetoothAdapter)
}
LaunchedEffect(key1 = selectedIndexOfAvailableItem) {
viewModel.handleTimeWarning(selectedIndexOfAvailableItem)
}
ComposableLifecycle { source, event ->
if (event == Lifecycle.Event.ON_PAUSE) {
viewmodel.stopScan()
} else if (event == Lifecycle.Event.ON_RESUME) {
viewModel.handleBluetoothScanState(rememberPairScreenState.bluetoothAdapter)
}
}
}
ComposableLifecycle
@Composable
fun ComposableLifecycle(
lifeCycleOwner: LifecycleOwner = LocalLifecycleOwner.current,
onEvent: (LifecycleOwner, Lifecycle.Event) -> Unit
) {
DisposableEffect(lifeCycleOwner) {
val observer = LifecycleEventObserver { source, event ->
onEvent(source, event)
}
lifeCycleOwner.lifecycle.addObserver(observer)
onDispose {
lifeCycleOwner.lifecycle.removeObserver(observer)
}
}
}
PairViewModel
class PairViewModel : BaseViewModel() {
companion object {
private const val WARNING_PERIOD_IN_MILLES: Long = 60000 // For 1 minute scan
const val DEVICE_NOT_SELECTED_INDEX = -1
}
var isBluetoothEnabled by mutableStateOf(false)
private set
private var job = Job()
get() {
if (field.isCancelled) field = Job()
return field
}
fun startScan(adapter: BluetoothAdapter) = viewModelScope.launch {
// start scan
}
fun stopScan(adapter: BluetoothAdapter) {
// stop scan
}
fun handleBluetoothScanState(bluetoothAdapter: BluetoothAdapter) {
if (isBluetoothEnabled) {
startScan(bluetoothAdapter)
} else {
cancelTimeWarning()
stopScan(bluetoothAdapter)
}
}
fun handleTimeWarning(selectedIndexOfAvailableItem: Int) {
if (selectedIndexOfAvailableItem == DEVICE_NOT_SELECTED_INDEX) {
startTimeWarning()
} else {
cancelTimeWarning()
}
}
fun startTimeWarning() {
viewModelScope.launch(job) {
delay(WARNING_PERIOD_IN_MILLES)
// make event here
}
}
internal fun cancelTimeWarning() {
if (job.isActive) {
job.cancel()
}
// make event here
}
}
Note bluetoothEnableState is Bluetooth ON and OFF i.e. true and false respectively.
I am listening bluetoothEnableState using LaunchedEffect when bluetooth state change ON or OFF. This is working fine. Now the problem occur,
1. when activity start the viewModel.handleBluetoothScanState(rememberPairScreenState.bluetoothAdapter) is called two times on onResume and LaunchedEffect.
2. when user disable bluetooth it state change to false and he goes to onPause. After coming back onResume handleBluetoothScanState will call and also change the bluetooth state to true then again call the LaunchedEffect.
I will explain more detail of 2nd condition
On Resume Close App Open App
Bluetooth disable -> On Pause -> OnResume -> Bluetooth enable
| | |
\ / \ / \ /
Launcheffect function Launcheffect
So I want to prevent to call double time function so any idea how can we solve this? Thanks
CodePudding user response:
I don't understand your use-case and I can't compile your code, but since you only want to startScan when blueTooth is ON and stopScan when blueTooth is OFF, I think you don't need to handle the same call/logic in your onResume
// I don't think you need it here,
else if (event == Lifecycle.Event.ON_RESUME) {
viewModel.handleBluetoothScanState(rememberPairScreenState.bluetoothAdapter)
}
since LaunchedEffect already reacts to bluetooth state changes ON(true)/OFF(false) and its already calling the same function in your ViewModel.
