-
Notifications
You must be signed in to change notification settings - Fork 23
Exception handling in launch
Devrath edited this page Jan 16, 2024
·
7 revisions
- UseCase-1: Without an Exception handler, One of the children throws an exception
- UseCase-2: Catching the exception locally in a child using try-catch
- UseCase-3: Try to catch on external catch
- UseCase-4: Using the exception handler
- 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
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
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()
}
}
- Note that even though
Child-2
throws exception, Since its caught, it will complete
Child-2 throws exception(Normal catch) Child-2 throws exception
Child-2 is complete
Child-1 is complete
Parent is complete
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 <------------->
}
- 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
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
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 <------------>
}
- Program will not crash but onCompletion blocks of corresponding coroutine is triggered from parent to child
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
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 <------------------->
}