reactormonk
03/19/2025, 1:11 PMSupervisorJob
and scope.launch(job) { delay(...); ... }
and then job.cancelChildren()
to cancel the delayed functions. Any better options?CLOVIS
03/19/2025, 1:17 PMcancel
on it
fun CoroutineScope.foo() {
val other = Job(coroutineContext.job)
launch(other) {
delay(…)
…
}
…
if (…) {
other.cancel()
}
}
reactormonk
03/19/2025, 1:19 PMlaunch
dispatch properly, or hang something somewhere while it's `delay()`ing?reactormonk
03/19/2025, 1:19 PMreactormonk
03/19/2025, 1:19 PMcoroutineScope { ... }
CLOVIS
03/19/2025, 1:22 PMlaunch
depends on the Dispatcher, but this code is the recommended way to do these kinds of thingsreactormonk
03/19/2025, 1:22 PMreactormonk
03/19/2025, 1:23 PMlifecycleScope
on android, and <http://Dispatcher.IO|Dispatcher.IO>
? Just to know some defaultsCLOVIS
03/19/2025, 1:23 PMfun CoroutineScope.foo() {
val other = launch {
delay(…)
…
}
…
other.cancel()
}
is better because you don't have to declare the job yourself (but is otherwise identical)reactormonk
03/19/2025, 1:24 PMother
outside of this scope, as another invocation of foo()
wants to cancel other
CLOVIS
03/19/2025, 1:24 PMfoo()
is called, the previous call is cancelled?reactormonk
03/19/2025, 1:25 PMCLOVIS
03/19/2025, 1:25 PMFlow<T>
that only emits the latest value
but otherwise, just store the Job
that launch
creates and call cancel
on it. If you store it as a local variable, do remember to Mutex
it thoughCLOVIS
03/19/2025, 1:26 PMLaunchedEffect
worksreactormonk
03/19/2025, 1:30 PMFlow
with collectLatest()
🤔
val transitionJob = SupervisorJob(lifecycleScope.coroutineContext.job)
lifecycleScope.launch {
hdmiService.events().collect { message ->
if (message.opcode != Constants.MESSAGE_REPORT_POWER_STATUS) {
Timber.tag("CEC").d { message.toString() }
}
when (message.opcode) {
Constants.MESSAGE_REPORT_POWER_STATUS -> {
if (message.source == <http://HdmiLogicalAddress.TV|HdmiLogicalAddress.TV>) {
val result: TVState =
when (val powerStatus = message.params.firstOrNull()?.toInt()) {
HdmiControlManager.POWER_STATUS_ON -> {
// TODO
// transitionJob.cancelChildren()
TVState.On
}
HdmiControlManager.POWER_STATUS_STANDBY -> {
transitionJob.cancelChildren()
TVState.Standby
}
HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON -> {
transitionJob.cancelChildren()
val heuristics = cecStateDetection?.tvTurnedOnDetectionHeuristics
Timber.d { "Heuristics is $heuristics" }
if (heuristics is TurnedOnDetectionHeuristics.DelayedAfterTransitionToOn) {
launch(transitionJob) {
Timber.d { "Delaying ${heuristics.delay} for TVState.On" }
delay(heuristics.delay)
Timber.d { "Delayed for TVState.On" }
cecFlows.tvState.emit(TVState.On)
}
}
TVState.TransientToOn
}
HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY -> {
transitionJob.cancelChildren()
val heuristics = cecStateDetection?.tvTurnedOffDetectionHeuristics
if (heuristics is TurnedOffDetectionHeuristics.TransitionToStandby) {
launch(transitionJob) {
delay(1.seconds)
cecFlows.tvState.emit(TVState.Standby)
}
}
TVState.TransientToStandby
}
HdmiControlManager.POWER_STATUS_UNKNOWN -> {
transitionJob.cancelChildren()
TVState.Unknown
}
else -> {
transitionJob.cancelChildren()
Timber.tag(TAG).w("Unknown response to query display status: $powerStatus")
TVState.Unknown
}
}
// TODO for testing purposes
if (result != TVState.On) {
cecFlows.tvState.emit(result)
}
}
}
reactormonk
03/19/2025, 1:31 PMcollectLatest()
or similar?CLOVIS
03/19/2025, 1:36 PM.cancel()
on the result of a .launch()
seems to be what you wantreactormonk
03/19/2025, 1:37 PMlaunch()
different than attaching it to a job
via launch(job)
?CLOVIS
03/19/2025, 1:37 PMCLOVIS
03/19/2025, 1:38 PMlaunch()
internally already creates a child job of the current scope, so you can use it instead of creating your own, but other than it's identical