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: trueoctylFractal
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 onStopstreetsofboston
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