Home > Blockchain >  Coroutine cancelled in ViewModel when coming back to screen
Coroutine cancelled in ViewModel when coming back to screen

Time:01-11

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. Why is this happening and how can I avoid it?

CodePudding user response:

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.

CodePudding user response:

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

  • Related