package nagochi.blog;

主にプログラミングの備忘録を書いていこうと思います。最近はAndroidアプリがメインです。

【RxJava】エラー発生時に retryWhen を使ってリトライダイアログを表示する

f:id:nagochi:20170314001251p:plain:w350

RxJava でエラーが発生したときに、もう一度リトライするか否かのダイアログを表示できないかと思い、方法を調べてみたときの備忘録。

RxJava ではエラーが発生した場合、例外処理として Subscriber 内の onError メソッドが即座に実行されます。

Observable
    .create(new Observable.OnSubscribe<String>() {
        @Override
        public void call(Subscriber<? super String> subscriber) {
            subscriber.onNext("test1");
            subscriber.onNext("test2");
            subscriber.onError(new Throwable("エラーテスト")); // エラーが発生
        }
    })
    .subscribe(new Subscriber<String>() {
        @Override
        public void onNext(String s) {
            System.out.println("onNext:" + s);
        }

        @Override
        public void onError(Throwable e) {
            System.out.println("onError:" + e.getMessage()); // 即座に例外処理が行われる
        }
        
        @Override
        public void onCompleted() {
            System.out.println("onCompleted");
        }
    });

実行結果:

onNext:test1
onNext:test2
onError:エラーテスト

この例外処理を行う前にリトライダイアログを挟みたいのですが、そのような場合は retrywhen オペレータを使用するといいようです。

retrywhen はエラーが発生した場合に例外処理よりも前に呼びだされ、本当に例外処理にエラーを流していいのか、それとももう一度リトライするのかを決めることができます。

これを利用し、表題のリトライダイアログを実装してみます。エラーが発生したらダイアログを表示し、「はい」を押したらリトライ、「いいえ」を押したら通常通り例外処理を行います。

Observable
    .create(new Observable.OnSubscribe<String>() {
        @Override
        public void call(Subscriber<? super String> subscriber) {
            subscriber.onNext("test1");
            subscriber.onNext("test2");
            subscriber.onError(new Throwable("エラーテスト"));
        }
    })
    .retryWhen(new Func1<Observable<? extends Throwable>, Observable<?>>() {
        @Override
        public Observable<?> call(final Observable<? extends Throwable> observable) {
            return observable.flatMap(new Func1<Throwable, Observable<?>>() {
                @Override
                public Observable<?> call(Throwable throwable) {
                    return Observable.create(new Observable.OnSubscribe<String>() {
                        @Override
                        public void call(Subscriber<? super String> subscriber) {
                            new AlertDialog.Builder(MainActivity.this)
                                    .setTitle("エラー")
                                    .setMessage("もう一度リトライしますか?")
                                    .setPositiveButton("はい", new DialogInterface.OnClickListener() {
                                        @Override
                                        public void onClick(DialogInterface dialog, int which) {
                                            subscriber.onNext(null);
                                        }
                                    })
                                    .setNegativeButton("いいえ", new DialogInterface.OnClickListener() {
                                        @Override
                                        public void onClick(DialogInterface dialog, int which) {
                                            subscriber.onError(throwable);
                                        }
                                    })
                                    .show();
                        }
                    });
                }
            });
        }
    })
    .subscribe(new Subscriber<String>() {
        @Override
        public void onNext(String s) {
            System.out.println("onNext:" + s);
        }

        @Override
        public void onError(Throwable e) {
            System.out.println("onError:" + e.getMessage());
        }
        
        @Override
        public void onCompleted() {
            System.out.println("onCompleted");
        }
    });

ラムダ式を使用してスッキリさせると

Observable
    .create(subscriber -> {
        subscriber.onNext("test1");
        subscriber.onNext("test2");
        subscriber.onError(new Throwable("エラーテスト"));
    })
    .retryWhen(observable ->
            observable.flatMap(throwable ->
                    Observable.create(subscriber ->
                            new AlertDialog.Builder(MainActivity.this)
                                    .setTitle("エラー")
                                    .setMessage("もう一度リトライしますか?")
                                    .setPositiveButton("はい", (dialog, which) ->
                                            subscriber.onNext(null)
                                    )
                                    .setNegativeButton("いいえ", (dialog, which) ->
                                            subscriber.onError(throwable)
                                    )
                                    .show()
                    )
            )
    )
    .subscribe(
            onNext -> System.out.println("onNext:" + onNext),
            onError -> System.out.println("onError:" + onError.getMessage()),
            () -> System.out.println("onCompleted")
    );

実行結果:

onNext:test1
onNext:test2
-> はい
onNext:test1
onNext:test2
-> はい
onNext:test1
onNext:test2
-> いいえ
onError:エラーテスト