简体   繁体   中英

Coroutine cancelled in ViewModel when coming back to screen

I have the following code in my view model.

    viewModelScope.launch {
        val response = request.invoke(coroutineScope)
        responseBlock?.invoke(response)
    }.apply {
        invokeOnCompletion {
            Log.e("Cancellation", "2---", it)
            if (showLoading) {
                loadingCount--
                changeLoadingIfNeeded()
            }
        }
    }

I'm using jetpack navigator and it work well the first time the screen is created, but when I go to another screen and come back to this one, the coroutine is cancelled with the following message kotlinx.coroutines.JobCancellationException: Job was cancelled; job=SupervisorJobImpl{Cancelled}@545aaed kotlinx.coroutines.JobCancellationException: Job was cancelled; job=SupervisorJobImpl{Cancelled}@545aaed . Why is this happening and how can I avoid it?

If you don't want your coroutines to be canceled when the Fragment or Activity is being destroyed, you have to untie them from Fragment's, Activity's, or ViewModel's lifecycle. For such cases, you can create a new coroutine scope to launch from; for example:

CoroutineScope(SupervisorJob()).launch(Dispatchers.IO) {
    val response = request.invoke(coroutineScope)
    responseBlock?.invoke(response)
}

There's a shortcut function if you want to do UI operations in your coroutine, which is equivalent to the above with the only difference that's being launched with Dispatchers.Main :

MainScope().launch {
    val response = request.invoke(coroutineScope)
    responseBlock?.invoke(response)
}

Keep in mind though that you are going to run into other issues if you aren't careful enough. Since your coroutine runs out of a lifecycle, if you are doing UI operations, you need to manually check for the following things:

  1. Does the view you are trying to change still exist?
  2. Does the fragment even still exist?
  3. Does the activity still exist?

Try not to keep any references of your Fragment or Activity in your coroutine; if you have to though, always use a WeakReference.

I fixed by changing

private val viewModel: MainViewModel by viewModel()

to

private val viewModel: MainViewModel by inject()

PS: I'm using koin as dependency injector

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM