ansman
12/21/2019, 8:14 PMval flow = repository
.observeStuff(id)
.map { it.map { it.createViewModel() }}
.combine(otherFlow) { stuff, otherStuff -> stuff to otherStuff }
// Don't emit until the settings are loaded
.onEach { settingsLoading.join() }
.conflate()
.flowOn(Dispatchers.Default)
.broadcastIn(viewModelScope, start = CoroutineStart.DEFAULT)
.asFlow()
scope.launch {
flow.collect {
// Rare crash because the scope is cancelled and the fragment is destroyed
view.update(it)
}
}
It really feels like it’s a bug in the coroutines framework because I can’t explain it otherwise
I can fix it by adding a yield
or if (coroutineContext[Job]?.isCancelled == true) throw CancellationException()
in my collect
but I really want to figure out what is happening. I’ve only seen it in Crashlytics
I added some logging to crashlytics to confirm the scope is cancelled:
CrashReporting.log("isStarted: %b, view == null: %b, isCancelled: %b".format(isStarted, view == null, coroutineContext[Job]!!.isCancelled))
And in the crash report is says:
isStarted: false, view == null: true, isCancelled: true
octylFractal
12/21/2019, 9:06 PMflow.collect
doesn't check for cancellation, and neither does most of the Flow methods (perhaps asFlow
would simply due to it using a channel underneath), so you must check for it manuallyansman
12/21/2019, 9:28 PMansman
12/21/2019, 9:28 PMansman
12/21/2019, 9:29 PMansman
12/21/2019, 9:29 PMstreetsofboston
12/21/2019, 9:40 PMviewModelScope
the same instance as scope
?
And, what is the stack-trace of the crash/exception you see?ansman
12/21/2019, 9:44 PMansman
12/21/2019, 10:01 PMstreetsofboston
12/21/2019, 10:02 PMasFlow()
should take care of that, 'un-subscribing' the flow it returns from the BroadcastChannel (returned by broadcastIn), when scope
is cancelled.
Maybe a bug...? At worst, your BroadcastChannel may suspend forever or memory issues (unlimited buffer), not the collect that keeps going after its scope
gets cancelled.
Just to be sure, have you checked that the scope
in your code is tied properly to that Fragment being destroyed.
Also, be sure to that your Fragment's 'CoroutineScope' is tied to its viewLifecycleOwner
, not to its lifecycleOwner
...ansman
12/21/2019, 10:03 PMansman
12/21/2019, 10:04 PMstreetsofboston
12/21/2019, 10:04 PMscope
to the Fragment's lifecycleOwner
or its viewLifecycleOwner
?ansman
12/21/2019, 10:05 PMonStart
and cancelled in onStop
streetsofboston
12/21/2019, 10:08 PMonStart
and onStop
of the Fragment :)
That should be working.... the view
of a Fragment doesn't get destroyed/recreated while in 'started' state.....ansman
12/21/2019, 10:09 PM