Search code examples
kotlinviewmodelkotlin-coroutines

viewModelScope SupervisorJob() cancelling the 3rd launch { } block


I was under the impression if i use viewModelScope which internally uses SupervisorJob(), will keep the 3rd launch { } block running an will print API 3 as well. But the output is different? Why is this happening?

class HomeViewModel : ViewModel() {

    private val _text = MutableLiveData<String>().apply {
        value = "This is home Fragment"
    }
    val text: LiveData<String> = _text

    val handler = CoroutineExceptionHandler { _, exception ->
        println("API exception caught: $exception")
        exception.printStackTrace()
    }
    fun test() {
        viewModelScope.launch(handler) {
            launch {
                println("API 1 launch executing")
                apiCall("API 1", 1000)
            }
            launch {
                println("API 2 launch executing")
                delay(1500)
                throw NullPointerException("Exception")
            }
            launch {
                println("API 3 launch executing")
                apiCall("API 3", 2000)
            }
            delay(4000)
            println("API no api its ended.")
        }
    }

    private suspend fun apiCall(message: String, time: Long) {
        delay(time)
        println(message)
    }
}

Output:

16:08:50.314  I  API 1 launch executing
16:08:50.314  I  API 2 launch executing
16:08:50.314  I  API 3 launch executing
16:08:51.317  I  API 1
16:08:51.819  I  API exception caught: java.lang.NullPointerException: Exception

Solution

  • Your three parallel coroutines are all children of the single parent coroutine because you used its scope to launch them. That scope of the parent coroutine is a child of viewModelScope and is not a supervisor so the child coroutines cannot fail independently.

    Either launch each of the parallel coroutines directly from viewModelScope or wrap the three of them with supervisorScope to create a local supervisor for launching them as children.

    Edit: Examples:

    viewModelScope.launch(handler) {
        viewModelScope.launch {
            println("API 1 launch executing")
            apiCall("API 1", 1000)
        }
        viewModelScope.launch {
            println("API 2 launch executing")
            delay(1500)
            throw NullPointerException("Exception")
        }
        viewModelScope.launch {
            println("API 3 launch executing")
            apiCall("API 3", 2000)
        }
        delay(4000)
        println("API no api its ended.")
    }
    

    Or

    viewModelScope.launch(handler) {
        supervisorScope {
            launch {
                println("API 1 launch executing")
                apiCall("API 1", 1000)
            }
            launch {
                println("API 2 launch executing")
                delay(1500)
                throw NullPointerException("Exception")
            }
            launch {
                println("API 3 launch executing")
                apiCall("API 3", 2000)
            }
        }
        delay(4000)
        println("API no api its ended.")
    }