2017년 6월 4일 일요일

[STUDY] RxAndroid Part 8 (Error 핸들링 방법)


[목차]==================================================
1. onError 해주지 않으면 Crash가 발생 주의
2. subscribe 에서 에러 처리
3. Error 캐치 - onErrorReturn
4. Error 캐치 - OnErrorResumeNext
5. Retry
6. Retry 횟수 제한
7. RetryWhen
======================================================


RxJava & RxAndroid 사용 시 항상 에러 핸들러를 구독하고 제공하는지 항상 확인해야 합니다. 그렇지 않으면 특히 Scheduler를 적용할 때 스택 트레이스에 아무것도 없을 수 있습니다. 물론 RxJava & RxAndroid 에서 뭔가 잘못됐다고 알려주긴 하지만 어디서 발생했는지 찾을 방법이 없습니다. 항상 에러 콜백을 사용하고, 에러가 발생한다면 에러를 로그로 남겨서 예상치 못한 오류를 기록해야 합니다.

1. onError 해주지 않으면 Crash가 발생 주의
 Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> subscriber) throws Exception {
                log("subscribe");
                subscriber.onNext("emit 1");
                subscriber.onNext("emit 2");
                subscriber.onError(new Throwable());
            }
        }).subscribe(new Consumer<String>() {
            @Override
            public void accept(String s) throws Exception {
                log("on next: " + s);
            }
        });

2. subscribe 에서 에러 처리

Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> subscriber) throws Exception {
                log("subscribe");
                subscriber.onNext("emit 1");
                subscriber.onNext("emit 2");
                subscriber.onError(new Throwable());
            }
        }).subscribe(new DefaultObserver<String>() {
            @Override
            public void onNext(String value) {
                log("on next: " + value);
            }
            @Override
            public void onError(Throwable e) {

                // 에러시 처리를 여기로 받음
                log("error:" + e);
            }
            @Override
            public void onComplete() {
                log("completed");
            }
        });
[출력결과]
subscribe
on next: emit 1
on next: emit 2
error:java.lang.Throwable

3. Error 캐치 - onErrorReturn
Observable 체인 안에서 발생한 Error 를 캐치해서, 대체할 Object로 변환하는 것으로 subscriber에 Error가 전달되는 것을 막을 수 있다.
 Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> subscriber) throws Exception {
                log("subscribe");
                subscriber.onNext("emit 1");
                subscriber.onNext("emit 2");
                subscriber.onError(new Throwable());            }
        }).onErrorReturn(new Function<Throwable, String>() {
            @Override
            public String apply(Throwable throwable) throws Exception {
                return "return";
            }
        }).subscribe(new DefaultObserver<String>() {
            @Override
            public void onNext(String value) {
                log("on next: " + value);
            }
            @Override
            public void onError(Throwable e) {
                // 에러시 처리를 여기로 받음
                log("error:" + e);
            }
            @Override
            public void onComplete() {
                log("completed");
            }
        });
[출력결과]
subscribe
on next: emit 1
on next: emit 2
on next: return
completed

4. Error 캐치 - OnErrorResumeNext
Observable 체인에서 발생한 Error를 캐치해서, 그 안에서 다시 한 번 Observable를 호출하면 에러시 대체 Stream을 반환할 수 있다.
 Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> subscriber) throws Exception {
                log("subscribe");
                subscriber.onNext("emit 1");
                subscriber.onNext("emit 2");
                subscriber.onError(new Throwable());            }
        }).onErrorResumeNext(new Function<Throwable, ObservableSource<? extends String>>() {            @Override
            public ObservableSource<? extends String> apply(Throwable throwable) throws Exception {
                return Observable.fromArray(new String[]{"resume 1", "resume 2"});
            }
        }).subscribe(new DefaultObserver<String>() {
            @Override
            public void onNext(String value) {
                log("on next: " + value);
            }
            @Override
            public void onError(Throwable e) {
                // 에러시 처리를 여기로 받음
                log("error:" + e);
            }
            @Override
            public void onComplete() {
                log("completed");
            }
        });
[출력결과]
subscribe
on next: emit 1
on next: emit 2
on next: resume 1
on next: resume 2
completed

5. Retry
Error가 일어났을 때, 자동으로 subscribe를 다시 해준다.
성공할때까지 계속... 무한루프 될 가능성이 있으므로 유의해야 한다
 Observable.create(subscriber -> {
            log("subscribe");
            subscriber.onNext("emit 1");
            subscriber.onNext("emit 2");
            subscriber.onError(new Throwable());
        })
        .retry()
        .subscribe()
[출력결과]
subscribe
on next: emit 1
on next: emit 2
subscribe
on next: emit 1
on next: emit 2
subscribe
on next: emit 1
on next: emit 2
.
.
.
반복

6. Retry 횟수 제한
 Observable.create(subscriber -> {
            log("subscribe");
            subscriber.onNext("emit 1");
            subscriber.onNext("emit 2");
            subscriber.onError(new Throwable());
        })
        .retry(3)
        .subscribe()
[출력결과]
subscribe
on next: emit 1
on next: emit 2
subscribe
on next: emit 1
on next: emit 2
subscribe
on next: emit 1
on next: emit 2
subscribe
on next: emit 1
on next: emit 2
error:java.lang.Throwable

Retry 좀 더 구체적인 설정
 Observable.create(subscriber -> {
            log("subscribe");
            subscriber.onNext("emit 1");
            subscriber.onNext("emit 2");
            subscriber.onError(new Throwable());        })
        .retry(new BiPredicate<Integer, Throwable>() {
            @Override
            public boolean test(Integer integer, Throwable throwable) throws Exception {
                if (integer < 3) {
                    return true;
                }
                return throwable instanceof IllegalStateException;
            }
        })

        .subscribe(s -> log("on next: " + s)
            , e -> log("error:" + e)
            , () -> log("completed"));
[출력결과]
subscribe
on next: emit 1
on next: emit 2
subscribe
on next: emit 1
on next: emit 2
subscribe
on next: emit 1
on next: emit 2
error:java.lang.Throwable

7. RetryWhen
보다 세밀하게 retry 처리를 제어하기 위한 함수.
 Observable.create(subscriber -> {
            log("subscribe");
            subscriber.onNext("emit 1");
            subscriber.onNext("emit 2");
            subscriber.onError(new Throwable());        }).retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
            @Override
            public ObservableSource<?> apply(Observable<Throwable> throwableObservable) throws Exception {
                return throwableObservable.flatMap(new Function<Throwable, ObservableSource<?>>() {
                    @Override
                    public ObservableSource<?> apply(Throwable throwable) throws Exception {
                        return Observable.timer(3, TimeUnit.SECONDS);
                    }
                });
            }
        }).subscribe(s -> log("on next: " + s)
                        , e -> log("error:" + e)
                        , () -> log("completed"));
[출력결과]
subscribe
on next: emit 1
on next: emit 2
// 3초 후
subscribe
on next: emit 1
on next: emit 2
// 3초 후
subscribe
on next: emit 1
on next: emit 2
.
.
.
반복

그냥 Error인채로 종료
 Observable.create(subscriber -> {
            log("subscribe");
            subscriber.onNext("emit 1");
            subscriber.onNext("emit 2");
            subscriber.onError(new Throwable());
        }).subscribeOn(AndroidSchedulers.mainThread())
                .retryWhen(throwableObservable -> throwableObservable.flatMap(
                        throwable -> Observable.error(throwable)
                ))
.subscribe(s -> log("on next: " + s)
                , e -> log("error:" + e)
                , () -> log("completed"));

Error에 대한 처리를 하지 않고 Complete하기
 Observable.create(subscriber -> {
            log("subscribe");
            subscriber.onNext("emit 1");
            subscriber.onNext("emit 2");
            subscriber.onError(new Throwable());
        }).onErrorResumeNext(throwable -> {Observable.empty();})                .subscribe(s -> log("on next: " + s)
                , e -> log("error:" + e)
                , () -> log("completed"));
[출력결과]
subscribe
on next: emit 1
on next: emit 2
completed

3번 retry하고 종료
이 경우, 앞의 retry(count) 함수와의 차이는 retry(count)에서는 retry 횟수가 제한에 도달한 후에 error로 종료합니다만, 이 케이스는 completed 에서 종료한다는 점이다.
 Observable.create(subscriber -> {
            log("subscribe");
            subscriber.onNext("emit 1");
            subscriber.onNext("emit 2");
            subscriber.onError(new Throwable());        }).retryWhen(throwableObservable -> throwableObservable.take(3))                .subscribe(s -> log("on next: " + s)
                , e -> log("error:" + e)
                , () -> log("completed"));
[출력결과]
subscribe
on next: emit 1
on next: emit 2
subscribe
on next: emit 1
on next: emit 2
subscribe
on next: emit 1
on next: emit 2
completed

3초 retry를 3번 하고 종료하기
 Observable.create(subscriber -> {
            log("subscribe");
            subscriber.onNext("emit 1");
            subscriber.onNext("emit 2");
            subscriber.onError(new Throwable());
        }).retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
            @Override
            public ObservableSource<?> apply(@NonNull Observable<Throwable> throwableObservable) throws Exception {
                return throwableObservable.take(3).flatMap(new Function<Throwable, ObservableSource<?>>() {
                    @Override
                    public ObservableSource<?> apply(@NonNull Throwable throwable) throws Exception {
                        return Observable.timer(3, TimeUnit.SECONDS);
                    }
                });
        
    }
        }).subscribe(s -> log("on next: " + s)
                        , e -> log("error:" + e)
                        , () -> log("completed"));
[출력결과]
subscribe
on next: emit 1
on next: emit 2
// 3초 후
subscribe
on next: emit 1
on next: emit 2
// 3초 후
subscribe
on next: emit 1
on next: emit 2
completed

 


 출처 : 인터넷에서 RxAndroid 검색하여 필요한 정보를 다양한 사이트에서 종합하여 작성된 것입니다. 많은 사이트 내용을 종합하여 공부하여 작성하다보니 일일이 나열하지 못하였습니다. ㅈㅅ(_ _) 이글은 자유롭게 퍼 가셔서 도움이 되었으면 좋겠습니다. 감사합니다. 

댓글 없음:

댓글 쓰기