Skip to content

Exception handling in launch

Devrath edited this page Jan 16, 2024 · 7 revisions

1_em1BIBosLwpbUQZZUEXgaw

Contents

UseCase-1: Without an Exception handler, One of the children throws an exception

Observation
  • Entire Program crashes with the exception
  • Also note Exception is propagated from children to parent
  • Parent invokeOnCompletion is invoked but with an exception
  • Then after Child-1 invokeOnCompletion is invoked but with an exception
  • But Child-2 invokeOnCompletion is not invoked since exception was thrown here but crash log is printed
Output
Parent throws exception Parent job is Cancelling
Child-1 throws exception Parent job is Cancelling
FATAL EXCEPTION: DefaultDispatcher-worker-3 java.lang.RuntimeException: Child-2 throws exception
Code
class UsingLaunchExceptionHandleDemoVm @Inject constructor( ) : ViewModel() {

    private val scopeJob = Job()

    private val ourScope =  CoroutineScope(scopeJob + Dispatchers.Default)

    fun demo() {

        ourScope.launch(CoroutineName("Parent")) {
            try {
                ourScope.launch(CoroutineName("Child-1")) {
                    try {
                        delay(10000)
                    }catch (ex : Exception){
                        println("Child-1 throws exception(Normal catch) ${ex.localizedMessage}")
                    }
                }.invokeOnCompletion { throwable ->
                    if(throwable!=null){
                        // Exception thrown
                        println("Child-1 throws exception ${throwable.localizedMessage}")
                    }else{
                        // Normal completion of coroutine
                        println("Child-1 is complete")
                    }
                }

                ourScope.launch(CoroutineName("Child-2")) {
                    throw RuntimeException("Child-2 throws exception")
                }.invokeOnCompletion{ throwable ->
                    if(throwable!=null){
                        // Exception thrown
                        println("Child-1 throws exception ${throwable.localizedMessage}")
                    }else{
                        // Normal completion of coroutine
                        println("Child-2 is complete")
                    }
                }
            }catch (ex : Exception){
                println("Parent throws exception(Normal catch) ${ex.localizedMessage}")
            }

            // Outer delay
            delay(15000)
        }.invokeOnCompletion{ throwable ->
            if(throwable!=null){
                // Exception thrown
                println("Parent throws exception ${throwable.localizedMessage}")
            }else{
                // Normal completion of coroutine
                println("Parent is complete")
            }
        }

    }


    fun rootCancel() {
        scopeJob?.cancel()
    }

}

UseCase-2: Catching the exception locally in a child using try-catch

Observation
  • Note that even though Child-2 throws exception, Since its caught, it will complete
Output
Child-2 throws exception(Normal catch) Child-2 throws exception
Child-2 is complete
Child-1 is complete
Parent is complete
Code
class UsingLaunchExceptionHandleDemoVm @Inject constructor( ) : ViewModel() {

    private val scopeJob = Job()

    private val ourScope =  CoroutineScope(scopeJob + Dispatchers.Default)
    
    // <----------------------->   Catch locally using try catch   <------------->
    fun demo2() {

        ourScope.launch(CoroutineName("Parent")) {
            try {
                ourScope.launch(CoroutineName("Child-1")) {
                    try {
                        delay(10000)
                    }catch (ex : Exception){
                        println("Child-1 throws exception(Normal catch) ${ex.localizedMessage}")
                    }
                }.invokeOnCompletion { throwable ->
                    if(throwable!=null){
                        // Exception thrown
                        println("Child-1 throws exception ${throwable.localizedMessage}")
                    }else{
                        // Normal completion of coroutine
                        println("Child-1 is complete")
                    }
                }

                ourScope.launch(CoroutineName("Child-2")) {
                    try {
                        throw RuntimeException("Child-2 throws exception")
                        delay(10000)
                    }catch (ex : Exception){
                        println("Child-2 throws exception(Normal catch) ${ex.localizedMessage}")
                    }
                }.invokeOnCompletion{ throwable ->
                    if(throwable!=null){
                        // Exception thrown
                        println("Child-1 throws exception ${throwable.localizedMessage}")
                    }else{
                        // Normal completion of coroutine
                        println("Child-2 is complete")
                    }
                }
            }catch (ex : Exception){
                println("Parent throws exception(Normal catch) ${ex.localizedMessage}")
            }

            // Outer delay
            delay(15000)
        }.invokeOnCompletion{ throwable ->
            if(throwable!=null){
                // Exception thrown
                println("Parent throws exception ${throwable.localizedMessage}")
            }else{
                // Normal completion of coroutine
                println("Parent is complete")
            }
        }

    }
    // <----------------------->   Catch locally using try catch   <------------->

}

UseCase-3: Try to catch on external catch

Observation
  • Entire Program crashes with the exception
  • So the exception is propagated from child to parent and then the parent invokeOnCompletion is called with exception
  • Child-1 invokeOnCompletion is not triggered but carries the exception
  • Note Child-2 invokeOnCompletion is not triggered
Output
Parent throws exception Parent job is Cancelling
Child-1 throws exception(Normal catch) Parent job is Cancelling
Child-1 throws exception Parent job is Cancelling
FATAL EXCEPTION: DefaultDispatcher-worker-1 java.lang.RuntimeException: Child-2 throws exception
Code
class UsingLaunchExceptionHandleDemoVm @Inject constructor( ) : ViewModel() {

    private val scopeJob = Job()

    private val ourScope =  CoroutineScope(scopeJob + Dispatchers.Default)

    
    // <----------------------->   Try to catch on external catch   <------------>
    fun demo3() {

        ourScope.launch(CoroutineName("Parent")) {
            try {
                ourScope.launch(CoroutineName("Child-1")) {
                    try {
                        delay(10000)
                    }catch (ex : Exception){
                        println("Child-1 throws exception(Normal catch) ${ex.localizedMessage}")
                    }
                }.invokeOnCompletion { throwable ->
                    if(throwable!=null){
                        // Exception thrown
                        println("Child-1 throws exception ${throwable.localizedMessage}")
                    }else{
                        // Normal completion of coroutine
                        println("Child-1 is complete")
                    }
                }
                try {
                    ourScope.launch(CoroutineName("Child-2")) {
                        throw RuntimeException("Child-2 throws exception")
                        delay(10000)
                    }.invokeOnCompletion{ throwable ->
                        if(throwable!=null){
                            // Exception thrown
                            println("Child-1 throws exception ${throwable.localizedMessage}")
                        }else{
                            // Normal completion of coroutine
                            println("Child-2 is complete")
                        }
                    }
                }catch (ex : Exception){
                    println("Child-2 throws exception(Outer normal catch) ${ex.localizedMessage}")
                }
            }catch (ex : Exception){
                println("Parent throws exception(Normal catch) ${ex.localizedMessage}")
            }

            // Outer delay
            delay(15000)
        }.invokeOnCompletion{ throwable ->
            if(throwable!=null){
                // Exception thrown
                println("Parent throws exception ${throwable.localizedMessage}")
            }else{
                // Normal completion of coroutine
                println("Parent is complete")
            }
        }

    }
    // <----------------------->   Try to catch on external catch   <------------>
}

UseCase-4: Using the exception handler

Observation
  • Program will not crash but onCompletion blocks of corresponding coroutine is triggered from parent to child
Output
Parent throws exception Parent job is Cancelling
Caught an exception: java.lang.RuntimeException: Child-2 throws exception
Child-1 throws exception Child-2 throws exception
Child-1 throws exception(Normal catch) Parent job is Cancelling
Child-1 throws exception Parent job is Cancelling
Code
class UsingLaunchExceptionHandleDemoVm @Inject constructor( ) : ViewModel() {

    private val scopeJob = Job()

    private val ourScope =  CoroutineScope(scopeJob + Dispatchers.Default)

    
    // <----------------------->   Using Exception Handler   <------------------->
    fun demo4() {

        val handler = CoroutineExceptionHandler { _, exception ->
            println("Caught an exception: $exception")
        }

        ourScope.launch(CoroutineName("Parent")) {
            try {
                ourScope.launch(CoroutineName("Child-1")) {
                    try {
                        delay(10000)
                    }catch (ex : Exception){
                        println("Child-1 throws exception(Normal catch) ${ex.localizedMessage}")
                    }
                }.invokeOnCompletion { throwable ->
                    if(throwable!=null){
                        // Exception thrown
                        println("Child-1 throws exception ${throwable.localizedMessage}")
                    }else{
                        // Normal completion of coroutine
                        println("Child-1 is complete")
                    }
                }
                try {
                    ourScope.launch(CoroutineName("Child-2") + handler) {
                        throw RuntimeException("Child-2 throws exception")
                        delay(10000)
                    }.invokeOnCompletion{ throwable ->
                        if(throwable!=null){
                            // Exception thrown
                            println("Child-1 throws exception ${throwable.localizedMessage}")
                        }else{
                            // Normal completion of coroutine
                            println("Child-2 is complete")
                        }
                    }
                }catch (ex : Exception){
                    println("Child-2 throws exception(Outer normal catch) ${ex.localizedMessage}")
                }
            }catch (ex : Exception){
                println("Parent throws exception(Normal catch) ${ex.localizedMessage}")
            }

            // Outer delay
            delay(15000)
        }.invokeOnCompletion{ throwable ->
            if(throwable!=null){
                // Exception thrown
                println("Parent throws exception ${throwable.localizedMessage}")
            }else{
                // Normal completion of coroutine
                println("Parent is complete")
            }
        }

    }
    // <----------------------->   Using Exception Handler   <------------------->
}
Clone this wiki locally