104
102

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

RxJavaを使った通信中にProgressダイアログを出す

Posted at

Retrofitをなどを使って非同期の通信を行うときに通信中はくるくるダイアログなどのProgressダイアログを出したいケースはよくあると思います。

ベタなやりかた

もっともベタなケースとして、通信を行うまえにダイアログを出し、onComplete()とonError()でダイアログを閉じるというやり方があります。

ベタなケース
    void onClick() {
        // ProgressDialogを生成して表示
        final ProgressDialog progressDialog = createProgressDialog();
        progressDialog.show();
        loadData().subscribe(new Subscriber<String>() {
            @Override
            public void onCompleted() {
                // complete時にProgressDialogを閉じる
                progressDialog.dismiss();
            }

            @Override
            public void onError(Throwable e) {
                // errorがあった時もProgressDialogを閉じる
                progressDialog.dismiss();
            }

            @Override
            public void onNext(String s) {

            }
        });
    }

この場合、onCompleted()onError()の両方でダイアログを閉じるコードを書く必要があります。また、もしloadData()が完了する前にunsubscribe()された場合はダイアログが閉じられずに残ってしまいます。従って途中でunsubscribe()される可能性がある場合は以下のようにunsubscribe()するときにダイアログも明示的に閉じてあげる必要があります。

cancel()でunsubscribe()されるケース
    private Subscription loadDataSubscription;

    /// progressDialogの参照はプロパティとして保持する必要がある
    private ProgressDialog progressDialog;

    void onClick() {
        progressDialog = createProgressDialog();
        progressDialog.show();
        loadDataSubscription = loadData().subscribe(new Subscriber<String>() {
            @Override
            public void onCompleted() {
                progressDialog.dismiss();
                progressDialog = null;
            }

            @Override
            public void onError(Throwable e) {
                progressDialog.dismiss();
                progressDialog = null;
            }

            @Override
            public void onNext(String s) {

            }
        });
    }

    private void cancel(){
        if(loadDataSubscription != null && !loadDataSubscription.isUnsubscribed()){
            loadDataSubscription.unsubscribe();
            progressDialog.dismiss();
            progressDialog = null;
        }
    }


これはめんどい、とてもめんどい

doOnSubscribe() / doOnUnsubscribe()を使う

ObservableにはdoOnSubscribe()/doOnUnsubscribe()というメソッドがあり、それぞれsubscribe時、unsubscribe時の処理を記述できます。

doOnSubscribe()/doOnUnsubscribe()を使った例
    void onClick() {
        final ProgressDialog progressDialog = createProgressDialog();
        loadData()
                .doOnSubscribe(new Action0() {
                    @Override
                    public void call() {
                        progressDialog.show();
                    }
                })
                .doOnUnsubscribe(new Action0() {
                    @Override
                    public void call() {
                        progressDialog.dismiss();
                    }
                })
                .subscribe(new Action1<String>() {
                    @Override
                    public void call(String s) {

                    }
                });
    }


これだと、ダイアログを閉じる処理をdoOnSubscribe()に集約できるのでそこら中に記述する必要がありませんし、処理中にunsubscribeされても安心です。
ただ、このやりかただとダイアログを表示する箇所でいちいちこの記述をする必要がありますし、ダイアログへの参照を外部に持たせる必要があります。

もうちょっとスマートに書きたい!

using()を使う

こういったケースで使われるユーティリティとしてusing()という関数があります。

using()とは

Observableと同じライフタイムであるリソースを生成/破棄することができるユーティリティ関数です。Observableが生成されるタイミングであるリソースを生成し、Observableが終了するタイミングでそのリソースを破棄します。

Using()

関数の定義

Using()
public static final <T,Resource> Observable<T> using(
      Func0<Resource> resourceFactory,
      Func1<? super Resource,? extends Observable<? extends T>> observableFactory,
      Action1<? super Resource> disposeAction)

引数

resourceFactory

Observableと連動するリソースを生成するファクトリ関数を

obesrvableFactory

Observableを生成するファクトリ関数

desposeAction

リソースを破棄する関数

using()をつかってProgressダイアログを表示する

このusing()関数をつかってダイアログの表示を実装すると以下のようになります。

using()を使ってProgressダイアログを表示する
    void onClick() {
        Observable.using(
                new Func0<ProgressDialog>() {
                    @Override
                    public ProgressDialog call() {
                        ProgressDialog progressDialog = createProgressDialog();
                        progressDialog.show();
                        return progressDialog;
                    }
                },
                new Func1<ProgressDialog, Observable<? extends String>>() {
                    @Override
                    public Observable<? extends String> call(ProgressDialog progressDialog) {
                        // 実際に行いたいデータ取得
                        return loadData();
                    }
                },
                new Action1<ProgressDialog>() {
                    @Override
                    public void call(ProgressDialog progressDialog) {
                        progressDialog.dismiss();
                    }
                })
                .subscribe();
    }

これで生成したProgressDialogはObservableで自動的に管理されることになり、どこかに参照を保持する必要がなくなりました。
でもまあ、あんまりdoOnSubscribe()doOnUnsubscribe()を使った場合と代わり映えしないです。どうせならこの処理を再利用できるように変えてしまいましょう。

Progressダイアログの表示処理を再利用できるように

using()からはVoidかなんかを返してその後flatMap()実際の通信処理を行うことによって、Progressダイアログの表示部分を再利用できるように切り離すことができます。

再利用可能なProgressダイアログ表示Observable

    void onClick() {
        usingProgressDialog()
                .flatMap(new Func1<Void, Observable<String>>() {
                    @Override
                    public Observable<String> call(Void aVoid) {
                        // flatMapでloadData()を実行
                        return loadData();
                    }
                })
                .subscribe();
    }

    // ProgressDialog表示するObservableを生成する
    private Observable<Void> usingProgressDialog() {
        return Observable.using(
                new Func0<ProgressDialog>() {
                    @Override
                    public ProgressDialog call() {
                        ProgressDialog progressDialog = createProgressDialog();
                        progressDialog.show();
                        return progressDialog;
                    }
                },
                new Func1<ProgressDialog, Observable<? extends Void>>() {
                    @Override
                    public Observable<? extends Void> call(ProgressDialog progressDialog) {
                        return Observable.just(null);
                    }
                },
                new Action1<ProgressDialog>() {
                    @Override
                    public void call(ProgressDialog progressDialog) {
                        progressDialog.dismiss();
                    }
                });
    }


これで、非常にシンプルに、Observableの処理にプログレスダイアログの表示を付加することができました。

こちらにサンプルコードを置いています。

104
102
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
104
102

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?