I have the following setup:
override suspend fun doWork(): Result = coroutineScope {
flowOfEvents
.onEach(eventChannel::send)
.launchIn(this)
if (isQueueEmpty()) return@coroutineScope Result.success()
...
}
What I'm seeing is the following: when isQueueEmpty()
is true, I return Result.success()
and I'd expect the flowOfEvents...launchIn(this)
stream to be disposed/cancelled as well, but I keep receiving events from that stream.
Am I doing something wrong?
Capturing the Job from launchIn(this)
and explicitly calling job.cancel()
before each return statement works, but feels unnecessary/wrong.
After a bit of research, it was made clear that this is a Kotlin coroutines behavior and isn't related to WorkManager
at all.
Flow<Event>
is a never-ending jobGiven these 2 points, it is now apparent why I kept receiving Event
s even after I returned from the parent coroutine.
The solution: call coroutineContext[Job]?.cancelChildren()
before returning from the parent coroutine.
override suspend fun doWork(): Result = coroutineScope {
flowOfEvents
.onEach(eventChannel::send)
.launchIn(this)
if (isQueueEmpty()) {
coroutineContext[Job]?.cancelChildren()
return@coroutineScope Result.success()
}
...
}
Note: calling parent's cancel()
throws a CancellationException
, which in the context of a WorkManager
meant that the Worker#doWork
method would actually result in a failed job.