[번역] RxJava를 사용하는 Android에서 에러 처리

[번역] RxJava를 사용하는 Android에서 에러 처리

Feb 26, 2017. | By: pluulove

본 포스팅은 RxJavaを使ったAndroidにおけるエラーハンドリング 포스팅을 번역했습니다.

제 일본어 실력으로 인하여 오역이나 오타가 발생할 수 있습니다.

실제 발표내용에 해당하는 슬라이드와 슬라이드의 일본어 부분만 번역만 번역했다는 점 양해바랍니다.

소개

RxJava Advent Calendar 2016 17일째입니다.

여러분, RxJava 사용하고 있습니까? 개인적으로도 일에서도 적극적으로 사용하고 있습니다.

이 문서에서는 RxJava를 사용한 Android의 오류 처리에 대해 쓰고 있습니다.

덧붙여서 나는 “Error Handing in RxJava”라는 제목으로 DroidKaigi 2017에 등장합니다.

이 기사는 발표 예정의 내용을 대략 정리한 것입니다.

오류 처리에서 자주 사용하는 Operator 소개

RxJava에는 몇 가지 오류 처리용으로 다음과 같은 Operator가 정의되어 있습니다.

  • onError
  • onErrorReturn
  • onErrorResumeNext
  • retry
  • retryWhen

onError

이것은 스트림에서 던져진 Exceptioin를 받기 위한 Operator입니다.

Observable observable = Observable.just(0);
observable
        .map(new Func1() {
            @Override
            public Object call(Object o) {
                throw new RuntimeException();
            }
        })
        .subscribe(new Subscriber() {
            @Override
            public void onCompleted() {}

            @Override
            public void onError(Throwable e) {}

            @Override
            public void onNext(Object o) {}
        });

map에서 throw된 Exception이 onError에 넘어옵니다.

onErrorReturn

이것은 Exception이 던져진 경우에 대신할 데이터를 흐르게 하기 위한 Operator입니다.

Observable<Integer> observable = Observable.just(0);
observable
        .map(new Func1<Integer, Integer>() {
            @Override
            public Integer call(Integer integer) {
                throw new RuntimeException();
            }
        })
        .onErrorReturn(new Func1<Throwable, Integer>() {
            @Override
            public Integer call(Throwable throwable) {
                return 10;
            }
        })
        .subscribe(new Action1<Integer>() {
            @Override
            public void call(Integer integer) {}
        });

map에서 Exception이 throw 되면 onErrorReturn가 호출되어 대신할 데이터로 “10”이 반환됩니다.

onErrorResumeNext

이것은 Exception이 던져진 경우에 대신 스트림을 흐르게 하기 위한 Operator입니다.

Observable<Integer> observable = Observable.just(0);
observable
        .map(new Func1<Integer, Integer>() {
            @Override
            public Integer call(Integer integer) {
                throw new RuntimeException();
            }
        })
        .onErrorResumeNext(new Func1<Throwable, Observable<? extends Integer>>() {
            @Override
            public Observable<? extends Integer> call(Throwable throwable) {
                return Observable.just(0);
            }
        })
        .subscribe(new Action1<Integer>() {
            @Override
            public void call(Integer integer) {}
        });

map에서 Exception이 throw 되면 onErrorResumeNext가 호출되어 대신할 스트림으로 “Observable.just(0)”이 반환됩니다.

retry

이것은 Exception이 던져진 경우에 재시도하기 위한 Operator입니다.

Observable<Integer> observable = Observable.just(0);
        observable
                .map(new Func1<Integer, Integer>() {
                    @Override
                    public Integer call(Integer integer) {
                        throw new RuntimeException();
                    }
                })
                .retry()
                .subscribe(new Action1<Integer>() {
                    @Override
                    public void call(Integer integer) {}
                });

map에서 Exception이 throw 되면 일반적으로는 스트림이 종료되지만, retry를 작성하는 것으로 다시 subscribe됩니다.

위의 코드는 무한 재시도되지만, 재시도 횟수를 설정할 수 있으며, 일반적으로 재시도 제한을 설정하는 것이 좋습니다.

retryWhen

retryWhen은 조금 이해하기 어렵지만, 재시도할 트리거가되는 스트림을 돌려주기 위한 Operator입니다.

retry의 경우 Exception이 던져진 순간에 재시도가 실행되지만, retryWhen을 사용하여 10초 정도 기다린 후 다시 시도하겠다는 식으로 재시도 타이밍을 세밀하게 제어할 수 있습니다. 10초 정도 기다린 후 다시 시도하는 코드는 다음과 같다.

Observable<Integer> observable = Observable.just(0);
        observable
                .map(new Func1<Integer, Integer>() {
                    @Override
                    public Integer call(Integer integer) {
                        throw new RuntimeException();
                    }
                })
                .retryWhen(new Func1<Observable<? extends Throwable>, Observable<?>>() {
                    @Override
                    public Observable<?> call(Observable<? extends Throwable> observable) {
                        return observable.flatMap(new Func1<Throwable, Observable<?>>() {
                            @Override
                            public Observable<?> call(Throwable throwable) {
                                return Observable.timer(10, TimeUnit.SECONDS);
                            }
                        });
                    }
                })
                .subscribe(new Action1<Integer>() {
                    @Override
                    public void call(Integer integer) {}
                });

유스 케이스

Android 어플리케이션에서는 다음과 같은 오류 핸들링이 자주 사용되고 있습니다. 이를 RxJava를 사용하여 구현하려 합니다.

  • Toast 표시하기
  • 재시도 여부를 사용자에게 묻는다

Toast 표시하기

이것은 간단하네요. onError에서 Toast를 표시할 뿐입니다.

Observable observable = Observable.just(0);
observable
        .map(new Func1() {
            @Override
            public Object call(Object o) {
                throw new RuntimeException();
            }
        })
        .subscribe(new Subscriber() {
            @Override
            public void onCompleted() {}

            @Override
            public void onError(Throwable e) {
                Toast.makeText(getApplicationContext(), "Error", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onNext(Object o) {}
        });

재시도 여부를 사용자에게 묻는다

이것은 조금 전 소개한 retryWhen을 사용합니다. retryWhen에서 Snackbar를 사용하여 사용자 재시도 여부를 묻습니다.

Observable<Integer> observable = Observable.just(0);
observable
        .map(new Func1<Integer, Integer>() {
            @Override
            public Integer call(Integer integer) {
                throw new RuntimeException();
            }
        })
        .retryWhen(new Func1<Observable<? extends Throwable>, Observable<?>>() {
            @Override
            public Observable<?> call(Observable<? extends Throwable> observable) {
                return observable.flatMap(new Func1<Throwable, Observable<?>>() {
                    @Override
                    public Observable<?> call(Throwable throwable) {
                        return Observable.create(new Observable.OnSubscribe<Void>() {
                            @Override
                            public void call(final Subscriber<? super Void> subscriber) {
                                Snackbar snackbar = Snackbar.make(view, "Retry?", Snackbar.LENGTH_INDEFINITE);
                                snackbar.setAction("Yes", new View.OnClickListener() {
                                    @Override
                                    public void onClick(View view) {
                                        subscriber.onNext(null);
                                    }
                                });
                                snackbar.show();
                            }
                        });
                    }
                });
            }
        })
        .subscribe(new Action1<Integer>() {
            @Override
            public void call(Integer integer) {}
        });

정리

이번에는 다음과 같은 오류 핸들링용 Operator를 소개했습니다.

  • onError
    • Exception을 받을 수 있다
  • onErrorReturn
    • Exceptioin이 던져진 경우에 대신할 데이터를 흘릴 수 있다
  • onErrorResumeNext
    • Exception이 던져진 경우에 대신할 스트림을 흘릴 수 있 있다
  • retry
    • Exception이 던져진 경우 재시도할 수 있다
  • retryWhen
    • 재시도 타이밍을 세밀하게 제어할 수 있다

위의 Operator를 고려하여 Android에서 자주 있는 오류 처리의 구현 예를 소개했습니다.

  • Toast 표시하기
    • onError에서 Toast 표시
  • 재시도 여부를 사용자에게 묻는다
    • retryWhen과 Snackbar을 결합한다

이 외에도 코멘트를 받을 수 있으면 구현 예를 추가하고 싶습니다.

그럼 좋은 RxJava 라이프를!

comments powered by Disqus

Currnte Pages Tags

Java RxJava

About

Pluu, Android Developer Blog Site

이전 블로그 링크 :네이버 블로그

Using Theme : SOLID SOLID Github

Social Links