Android/RxJava

Android RxJava - 예외 처리

강태종 2022. 3. 8. 18:01

RxJava 예외 처리

기본적으로 Observer의 onError 함수를 통해서 에러를 처리할 수 있습니다. onError로 에러를 처리하는 경우 스트림은 데이터 발행을 중단하며 Observer는 onComplete 호출을 하지 못합니다.

fun onError() {
    Observable.create<String> { emitter ->
        emitter.onNext("Message 1")
        emitter.onNext("Message 2")
        throw Exception("Something Wrong")
    }.subscribe({
        println(it)
    }, {
        println("Error : $it")
    })
}
Message 1
Message 2
Error : java.lang.Exception: Something Wrong

 

Rx에서는 onError를 사용한 경우 데이터 스트림이 손상되어 복구할 수 없다고 표현하고, 이러한 방법을 최후의 수단이라고 설명하고 있습니다. Rx는 연산자를 통해 손상을 복구할 수 있으며, 전역으로 에러를 관리할 수 있습니다.

 

아래의 방법은 공식문서에서 권장하는 손상 복구 방법입니다. (링크)

  • Error를 삼키고 흐름을 계속 할 수 있는 백업 Observable로 교체한다.
  • Error를 삼키고 기본 값을 발행한다.
  • Error를 삼키고 즉시 실패한 Observable를 다시 시작한다.
  • Error를 삼키고 일정 시간 기다렸다가 실패한 Observable를 다시 시작한다.

onErrorResumeNext

에러가 발생했을 때 백업 Observable로 교체하는 방법입니다.

fun onErrorResumeNext() {
    Observable.create<String> { emitter ->
        emitter.onNext("Message 1")
        emitter.onNext("Message 2")
        throw Exception("Something Wrong")
    }.onErrorResumeNext {
        Observable.just(it.message ?: "")
    }.subscribe({
        println("onNext : $it")
    }, {
        println("onError : $it")
    }, {
        println("onComplete")
    })
}
onNext : Message 1
onNext : Message 2
onNext : Something Wrong
onComplete

 

onErrorReturn

에러가 발생했을 때 기본 값을 반환하는 방법입니다.

fun onErrorReturn() {
    Observable.create<String> { emitter ->
        emitter.onNext("Message 1")
        emitter.onNext("Message 2")
        throw Exception("Something Wrong")
    }.onErrorReturn {
        "Message ${it.message}"
    }.subscribe({
        println("onNext : $it")
    }, {
        println("onError : $it")
    }, {
        println("onComplete")
    })
}
onNext : Message 1
onNext : Message 2
onNext : Message Something Wrong
onComplete

 

retry

에러가 발생했을 때 즉시 재시도하는 방법입니다. 횟수나, Exception으로 재시도 여부를 판단할 수 있습니다.

fun retry() {
    Observable.create<String> { emitter ->
        emitter.onNext("Message 1")
        emitter.onNext("Message 2")
        throw Exception("Something Wrong")
    }.retry().subscribe({
        println("onNext : $it")
    }, {
        println("onError : $it")
    }, {
        println("onComplete")
    })
}
onNext : Message 1
onNext : Message 2
onNext : Message 1
onNext : Message 2
...

 

retryWhen

retry에 좀 더 조건을 줄 수 있습니다.

 

take를 통해 retry count를 정하고, flatMap, timer를 통해 대기 시간을 설정했습니다.

fun retryWhen() {
    Observable.create<String> { emitter ->
        println("Observable")
        emitter.onNext("Message 1")
        emitter.onNext("Message 2")
        throw Exception("Something Wrong")
    }.retryWhen {
        it.take(3).flatMap {
            Observable.timer(3, TimeUnit.SECONDS)
        }
    }.subscribe({
        println("onNext : $it")
    }, {
        println("onError : $it")
    }, {
        println("onComplete")
    })

    Thread.sleep(30000L)
}
Observable
onNext : Message 1
onNext : Message 2
Observable
onNext : Message 1
onNext : Message 2
Observable
onNext : Message 1
onNext : Message 2
Observable
onNext : Message 1
onNext : Message 2
onComplete

전역 이벤트

에러가 발생했을 때 전역에서 공통으로 발생하는 이벤트는 하나의 클래스에서 관리하는 것이 효율적입니다. 여러 곳에 코드를 입력하면 중복된 코드가 발생하고 유지 보수에 어려움이 있습니다. 예를 들어 에러가 발생했을 때 서버로 로그를 보내거나, 통계를 집계하는 경우가 있습니다.

 

fun setErrorHandler() {
    RxJavaPlugins.setErrorHandler {
        println("ErrorHandler : ${it.message}")
    }

    Observable.create<String> { emitter ->
        emitter.onError(Exception("Something Wrong"))
    }.subscribe {
        println(it)
    }
}
ErrorHandler : The exception was not handled due to missing onError handler in the subscribe() method call. Further reading: https://github.com/ReactiveX/RxJava/wiki/Error-Handling | java.lang.Exception: Something Wrong