Jeff Johnson
05/06/2020, 5:43 PMView that does not exist anymore.
open class CoroutineLauncher : CoroutineScope {
private val dispatcher: CoroutineDispatcher = Dispatchers.Main
private val supervisorJob = SupervisorJob()
override val coroutineContext: CoroutineContext
get() = dispatcher + supervisorJob
fun launch(action: suspend CoroutineScope.() -> Unit) = launch(block = action)
fun cancelCoroutines() {
supervisorJob.cancelChildren() //coroutineContext.cancelChildren() has same results
}
}
here is the usage
class MyFragment : Fragment {
val launcher = CoroutineLauncher()
fun onSomeEvent() {
launcher.launch {
val result = someSuspendFunction() // made suspend function by using withContext(<http://Dispatchers.IO|Dispatchers.IO>)
if (!isActive) return
// CAUSES CRASH
myTextView.text = result.userText
}
}
override fun onDestroyView() {
super.onDestroyView()
launcher.cancelCoroutines()
}
}
I added log lines to ensure onDestroyView and cancelCoroutines are both being called before the crash. It is my understanding that withContext is cancellable and thus I shouldn’t even need to check for isActive . I feel like I’m missing something obvious, but I can’t figure out what it isJoffrey
05/06/2020, 6:51 PMmyTextView may not be available when you reach that line. If your coroutine is cancelled right before this call, it will still try to perform the assignment (cancellation is cooperative).
Maybe you should not try to update the UI from the `launch`’s context. Use a withContext to ensure it runs on the UI thread.
Another way would be to use cancelAndJoin() to make sure you wait for your coroutines to finish from onDestroyViewJeff Johnson
05/06/2020, 7:44 PMmyTextView is unavailable. My CoroutineLauncher uses Dispatchers.Main as its dispatcher so that should definitely run on the UI threadJeff Johnson
05/06/2020, 7:46 PMcancelChildren is called before someSuspendFunction() is even called. Which since withContext is ‘cooperative’ should mean that the Text View ix never even accessedJeff Johnson
05/06/2020, 9:12 PMonSomeEvent was being invoked after cancel was called. Since we call cancelChildren instead of cancel , the launcher does not refuse new Jobs, and since cancel already happened, the new coroutine runs like normal and crashesJoffrey
05/06/2020, 10:16 PM