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.