Coroutine Exception

: ) YOUNG·2023년 12월 10일
1

비동기 처리

목록 보기
1/1
post-thumbnail

CallFromWrongThreadException

안드로이드 뷰 계층을 생성하지 않은 쓰레드가 관련 뷰를 업데이트하려고 할 때마다 CalledFromWrongThreadException을 발생시킨다.

이 예외는 UI쓰레드가 아닌 다른 쓰레드가 뷰를 업데이트 할 때마다 발생한다.
UI쓰레드만이 뷰 계층을 생성하고 업데이트 할 수 있다.




NetworkOnMainThreadException

자바에서의 네트워크 동작은 기본적으로 블로킹된다.

UI쓰레드가 블로킹 된다는 것은 애니메이션이나 기타 상호작용을 포함한 모든 UI가 멈춘다는 것을 의미하므로, UI 쓰레드에서 네트워크 작업을 수행할 때 마다 안드로이드는 중단된다.

이런 상황이 발생할 때마다 NetworkOnMainThreadException이 발생하는데 백그라운드 쓰레드를 사용해서 사용자의 경험을 개선해야 한다.

⭐ 백그라운드에서 요청하고 UI쓰레드에서 업데이트 할 것




Coroutine 결과와 에러 처리

결과 처리를 위한 목적으로 코루틴을 시작했다면 async()를 사용해야 한다.
async()Defferred<T>를 반환하는데, 디퍼드 코루틴 프레임워크에서 제공하는 취소 불가능한 넌 블로킹 퓨처를 의미하며, T는 결과의 유형을 나타낸다.


fun main() = runBlocking {
    val task = GlobalScope.async {
        doSomething()
    }


    task.join()
    println("Completed")

} // End of main

private fun doSomething() {
    throw UnsupportedOperationException("Can't do")
} // End of doSomething()

Exception이 발생하지 않았고 결과를 출력했다.




async() 블록 안에서 발생하는 예외는 그 결과에 첨부되는데, 그 결과를 확인해야 예외를 찾을 수 있다.
이를 위해서 isCancelledgetCancellationException() 메소드를 함께 사용해 안전하게 예외를 가져올 수 있다.


fun main() = runBlocking {
    val task = GlobalScope.async {
        doSomething()
    }

    task.join()
    if (task.isCancelled) {
        val exception = task.getCancellationException()
        println("Error with message : ${exception.cause}")
    } else {
        println("Success")
    }
} // End of main

이렇게 task.isCancelled를 사용해서 오류를 출력할 수 있다.




예외를 전파하기 위해서는 Defferred에서 await()을 호출할 수 있다.


fun main() = runBlocking {
    val task = GlobalScope.async {
        doSomething()
    }

    task.await()
    println("Completed")
} // End of main

await()을 호출해서 중단되는데 이 경우가 예외를 감싸지 않고 전파하는, unwrapping deferred이다.

join()으로 대기한 후 검증하고 어떤 오류를 처리하는 것과 await()을 직접 호출하는 방식의 주요 차이는 join()은 예외를 전파하지 않고 처리하는 반면, await()은 단지 호출하는 것만으로 예외가 전파된다는 점이다.

await()를 사용한 예제는 실행 중 에러를 의미하는 코드 1을 반환하는 반면, join()으로 대기하고 iscancelledgetCancellaaationException()을 사용해 에러를 처리한 경우는 성공을 의미하는 코드 0이 나온다.




launch 코루틴

결과를 반환하지 않는 코루틴을 시작하려면 launch()를 사용해야 한다. launch()는 연산이 실패한 경우에만 통보 받기를 원하는 fire-and-forget 시나리오를 위해 설계되었으며, 필요할 때 취소할 수 있는 함수도 함께 제공된다.

fire-and-forget scenario, 이벤트나 메세지 기반 시스템에서 널리 활용되는 패턴으로, 미사일을 발사하고 나면 그 후 미사일은 알아서 표적을 향해 날아가는데 마시일에 대해 잊고 있어도(Forget) 알아서 표적에 명중한다는 것으로, 여기서는 실행 후 결과에 대해 신경 쓸 필요 없는 경우를 의미한다.


fun main() = runBlocking {
    val task = GlobalScope.launch {
        doSomething()
    }

    task.join()
    println("Completed")
} // End of main

예상한 대로 예외가 스택에 출력되지만 실행이 중단되지 않았고, main()이 실행 완료되었다는 메세지가 출력되었다.

0개의 댓글