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


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

# ベタなやりかた

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


```java:ベタなケース
    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()するときにダイアログも明示的に閉じてあげる必要があります。

```java: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時の処理を記述できます。

```java: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()](http://reactivex.io/documentation/operators/images/using.c.png)


### 関数の定義

```java: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()`関数をつかってダイアログの表示を実装すると以下のようになります。

```java: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ダイアログの表示部分を再利用できるように切り離すことができます。

```java:再利用可能な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の処理にプログレスダイアログの表示を付加することができました。


こちらに[サンプルコード](https://github.com/yamamotoj/rx-progress-dialog-sample)を置いています。