RxJava+RxAndroid+RxLifecycleを使った非同期処理を行う

やぎすけAdventCalendar2016 Android

Posted on Dec 11


こんにちは、やぎにいです。
やぎすけ Advent Calendar 2016の11日目です。
書き溜めもほとんどない(ネタリストだけは確保している)ので厳しくなってくる今日このごろです。

昨日は僕がJavaでリストを操作するときにfor文をやめてみるを書きました。

今日はAndoirdでAPI通信を行って結果を非同期で処理します。

はじめに

今日やる環境として使うライブラリとバージョンを載せておきます。
おわりにコードも載せているので合わせて御覧ください。

  • RxJava v1.1.6
  • RxAndroid v1.2.1
  • RxLifecycle v1.0

本題に関係ないのでリストには入れてませんが通信はRetrofitを使います。

今回のゴールはこれらを使ってお天気Webサービス仕様 - Weather Hacks - livedoor 天気情報のAPIを使用し、横浜のみなとみらいの今日明日明後日の天気を表示するアプリを作成しましょう。


Rx(Java|Android|Lifecycle)とは

RxJava & RxAndroid

RxJavaとはJavaでリアクティブプログラミングを行うためのライブラリです。リアクティブプログラミングは、所謂データが流れてくるような感じでそのデータを受け取る度に様々なものが反応(リアクション)するようなやつです(かなりざっくり)。
ストリームの考え方については昨日のJavaでリストを操作するときにfor文をやめてみるで実際にJava8にあるStreamを使ってリストを操作しました。
基本的にはああいう奴です。何かしらのデータが流れてきて、それを加工なりして受け取ったタイミングで処理をする(出力等)奴にはもってこいです。

実は昨日いろいろ紹介した中でRxJavaではリスト操作について説明しませんでしたが、もちろんRxJavaでも昨日のような事を出来ます。
1〜10までの整数を受け取って、2倍に加工して、出力するのようなものだったら。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Observable.from(new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
    .map(new Func1<Integer, Integer>() {
        @Override
        public Integer call(Integer i) {
            return i * 2;
        }
    })
    .subscribe(new Observer<Integer>() {
        @Override
        public void onNext(Integer i) {
            Log.d("VALUE", i.toString());
        }

        @Override
        public void onCompleted() {
            Log.d("DONE", "Complete");
        }

        @Override
        public void onError(Throwable e) {
            Log.e("ERROR", e.toString)
        }
    });

の様に書くことが出来ます。
今日はAndroidの話なので出力はLogにしました。
大体昨日のと同じですね。リストがあり、mapで加工し、それを流す。
詳しいメソッドに関しては後で紹介します。

RxAndroidRxJavaをAndroidで利用するために特化した感じのライブラリです。RxJava自体はJavaのライブラリなのでAndroidプロジェクトでなくとも使うことが出来ます。

RxLifecycle

RxLifecycleはメモリリークなどを防ぐユーティリティのライブラリです。
Androidでは通信処理はメインスレッドで行うことが出来ず、AsyncTaskなどを使って非同期処理をすることになりますが、その際に結果はActivityなどで弄りたい場合によくCallbackなどを使います。 そしてCallbackを使う際に戻ってくる予定だったActivityが破棄されてしまっている状態だと落ちてしまいます。

そんなときにRxLifecycleを使用し、RxJavaの処理をAndroidのFragmentActivityのライフサイクルにバインドすることによって、そのタイミングで処理を中断させることが出来ます。
通信途中にユーザーがホーム画面に戻っても適切に終了させることができるのです。素晴らしい。
今回は以上の事を踏まえて、お天気APIに通信する→戻ってきたデータをViewにセットする。という流れを作ってみたいと思います。

通信する

早速ですが、通信しましょう。
前提条件として、Retrofitを使っています。APIインターフェースの戻り値をObservableにすることによってRxJavaを簡単に使っています。

1
2
@GET("json/v1")
Observable<WeatherModel> getWeather(@Query("city") String cityNumber);

WeatherModelというのはお天気APIの返すjsonをオブジェクトに変換するモデルです。気になる方はお天気Webサービス仕様 - Weather Hacks - livedoor 天気情報を見てください。 ここのレスポンスフィールド通りに作りました。実際のモデルクラスはWetherModel.javaです。

まずは通信して処理する完成形をペタリ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
retrofit.create(WeatherApi.class).getWeather(CITY_NUMBER)
    .subscribeOn(Schedulers.io())
    .toSingle()
    .compose(this.bindToLifecycle().<WeatherModel>forSingle())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new SingleSubscriber<WeatherModel>() {
        @Override
        public void onSuccess(WeatherModel weather) {
            // ここでViewなどに返却されたデータをセットする 以下は例

            textView.setText(weather.description.text);
            unsubscribe();
        }

        @Override
        public void onError(Throwable e) {
            if (e instanceof CancellationException) {
                unsubscribe();
                return;
            }
            unsubscribe();
        }
    });                  

1つずつ噛み砕いていきましょう。 最初の.create行はただのAPI通信を開始するRetrofitの部分なので割愛するとして次の行から

  • .subscribeOn(Schedulers.io())

subscribeOn()はRxJavaの機能で、ここでは通信部をどういうスレッドで行うかを指定しています。
Schedulers.io()は標準で用意されているSchedulersであり、I/Oバウンド用のスレッドを生成して行います。

  • .toSingle()

toSingle()SingleSubscriberというものに変換してくれて、これは値が1度しか流れてこないようなものに使います。
このAPIではAPIを通信したらjsonが1つしか流れてこないのでこれを使用しています。
最初のリスト操作の例で上げたようなもので使うと1,2,3...,10と合計10回流れてくるので使えません。

  • .compose(thi.bindToLifecycle().<WeatherModel>forSingle())

compose()Observableに作用させるものであり、今回はRxLifecyclebindToLifecycleという機能を使って居ます。
bindToLifecycleはその名前のとおりAndroidのライフサイクルにバインドするものです、それをcompose()に使うことによって「このObservableはライフサイクルにバインドされたぞ」という形になります。
Activityなどが破棄されたらそれに従ってObservableも破棄される形です(厳密にはちょっと違いますが、RxLifecycleに関しては明日また別の記事を書くのでそこで説明します)

  • .observeOn(AndroidSchedulers.mainThread())

おっと?subscribeOn()というのがあったじゃないかと思われるかもしれませんが、observeOn()subscribeOn()は作用の仕方が違います。
subscribeOn()は先ほど説明したとおり、今回で言うところのAPI通信部のスケジューラを決めています。
observeOn()はそれを書いた以降の処理のスケジューラを決めています。今回はAndroidSchedulers.mainThread()としてメインスレッドでViewとかに返ってきた値をセットしますよという感じです。

  • .subscribe(new SingleSubscriber<WeatherModel>())

所謂処理開始のような形です。今回はSingleSubscriberを使用しているのでメソッドは上記のonSuccess()onError()の2つです。メソッド名からしてわかりますね。
それぞれのメソッド内で書かれている部分はRxLifecycleに関係してくることなので、詳しくは明日の記事で書きます。

大体通信部の雰囲気はつかめたでしょうか。
これでAPIを叩いて戻ってきた値を処理することが完了しました。めちゃくちゃ簡単ですね!
以上の通信を使って実際に作ってみたサンプルアプリがこちらになります。

上記コードのonSuccess内でViewに値をセットしています。
各天気はさくっとRecyclerViewを使ってリストを作成するで紹介したRecyclerViewを使っています。なんかこのAdvent Calendarの復習みたいになっていますね。

このアプリのソースはyagi2/rxsampleにおいておきました。Android Studioなどでインポートするとすぐに試すことが出来ます。
APIのエンドポイントとベースURLとモデルを書き換えれば任意の好きなAPIで通信できますね!

おわりに

かなりざっくりと駆け足で紹介してしまいましたが、RxJava+RxAndroid+RxLifecycleでのAPI通信非同期処理のやりかたでした。
今まではAsyncTaskの通信クラスを作りそれに対して.execute()を行ってコールバックで処理する、のような手法をやっていましたが、クラスが増える、コールバックがめちゃくちゃになってしまっているなどを考えると読みやすさ的にもこちらのほうが個人的には好きです。

明日はRxLifecycleを掘り下げた話題をちょっとしてみようと思っています。

以上、やぎにいでした!


このエントリーをはてなブックマークに追加
comments powered by Disqus

<< RxLifecycleでのライフサイクルへのバインドについて     Javaでリストを操作するときにfor文をやめてみる >>



2018やぎ小屋