Lilly
08/30/2021, 5:08 PM// In ViewModel
fun toggleScan() {
when {
isScanning.value -> stopScan()
else -> {
startScan()
presenterScope.launch(Dispatchers.Default) {
delay(DISCOVERY_TIMEOUT)
}
}
}
}
What is an elegant way to avoid creating a new coroutine on every button click?Zach Klippenstein (he/him) [MOD]
08/30/2021, 5:30 PMLilly
08/30/2021, 6:23 PMstartScan()
) is running until the timeout runs out. Correct me if I'm wrong but when I click the button again following happens:
--startScan--------------------------------------------------stopScan
-----------------startScan----------------------------------------x----------stopScan
---------------------------------startScan----------------------------------------x---------------stopScan
On x, the state is no longer consistent. In the past I have experienced unwanted behavior with not canceling the active coroutineZach Klippenstein (he/him) [MOD]
08/30/2021, 6:50 PMOn x, the state is no longer consistentBecause
isScanning.value
isn’t updated?Lilly
08/30/2021, 8:19 PM// Also in ViewModel
fun startScan() {
presenterScope.launch { discoverBtDevicesTask.startDiscovery() }
isScanning.value = true
}
fun stopScan() {
presenterScope.launch { discoverBtDevicesTask.stopDiscovery() }
isScanning.value = false
}
alex
08/30/2021, 9:36 PMLilly
08/30/2021, 10:32 PMlateinit var timerJob: Job
fun toggleScan() {
if (isScanning.value) {
timerJob.cancel()
stopScan()
} else {
startScan()
// Scan for [DISCOVERY_TIMEOUT] seconds
timerJob = presenterScope.launch(Dispatchers.Default) {
delay(DISCOVERY_TIMEOUT)
}
timerJob.invokeOnCompletion { stopScan() }
}
}
But it doesn't feel right. Is there room for improvement?louiscad
08/31/2021, 6:54 AMlouiscad
08/31/2021, 6:58 AMawaitOneClick()
(there are ways to get something similar in Compose with MutableState
), and it allows just that, disabling the button after click, until it's called again by default. https://splitties.louiscad.com/modules/views-coroutines/#content
Here's the permalink to the code if you are interested but don't feel like adding the dependency, or are just curious about the implementation: https://github.com/LouisCAD/Splitties/blob/1ea7e072ae7fba5b989f226dd8371cd22d0916e[…]idMain/kotlin/splitties/views/coroutines/VisibilityAndClicks.ktLilly
08/31/2021, 1:22 PMisScanning
is true. When clicked again, discovery is stopped, isScanning
is false, when not clicked, isScanning
is false after timeout. When I don't cancel the active coroutine following should happen in theory (explains the strange behavior I experienced):
First click, isScanhning
is true, coroutine A is runnung
Second click, isScanning
is false, coroutine A + B is running
Third click, isScanning
is true, coroutine A + B + C is running. This time I wait for timeout but since A is still running, isScanning
is changed to false too early.Lilly
08/31/2021, 2:25 PMExtendedFloatingActionButton(
onClick = { onToggleScan() },
)
Zach Klippenstein (he/him) [MOD]
08/31/2021, 4:58 PMdarkmoon_uk
09/01/2021, 6:22 AMFlow<Unit>
, that way you can debounce()
them and flat-map to the discovery e.g:
private val clicksFlow = MutableSharedFlow<Unit>()
fun onClicked() = clicksFlow.tryEmit(Unit)
val discoveredFlow: Flow<DiscoveryResults> =
clicksFlow
.debounce(100)
.flatMapLatest() {
beginDiscoveryProcess()
}
...meaning that if the button is clicked again, any ongoing discovery process is 'automatically' cancelled (that's the -Latest
part) and a new one started.Lilly
09/05/2021, 1:05 AM-Latest
part.
Are there any other constructs, that cancel and start a coroutine like -Latest
?darkmoon_uk
09/05/2021, 5:16 AMmapLatest
which may be more suitable depending on your scanning API