こんにちは、やぎにいです。
やぎすけ Advent Calendar 2016の11日目です。
書き溜めもほとんどない(ネタリストだけは確保している)ので厳しくなってくる今日このごろです。
昨日は僕がJavaでリストを操作するときにfor文をやめてみるを書きました。
今日はAndoirdでAPI通信を行って結果を非同期で処理します。
今日やる環境として使うライブラリとバージョンを載せておきます。
おわりにコードも載せているので合わせて御覧ください。
本題に関係ないのでリストには入れてませんが通信はRetrofit
を使います。
今回のゴールはこれらを使ってお天気Webサービス仕様 - Weather Hacks - livedoor 天気情報のAPIを使用し、横浜のみなとみらいの今日明日明後日の天気を表示するアプリを作成しましょう。
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
で加工し、それを流す。
詳しいメソッドに関しては後で紹介します。
RxAndroid
はRxJava
をAndroidで利用するために特化した感じのライブラリです。RxJava
自体はJavaのライブラリなのでAndroidプロジェクトでなくとも使うことが出来ます。
RxLifecycle
はメモリリークなどを防ぐユーティリティのライブラリです。
Androidでは通信処理はメインスレッドで行うことが出来ず、AsyncTask
などを使って非同期処理をすることになりますが、その際に結果はActivity
などで弄りたい場合によくCallback
などを使います。
そしてCallback
を使う際に戻ってくる予定だったActivity
が破棄されてしまっている状態だと落ちてしまいます。
そんなときにRxLifecycle
を使用し、RxJava
の処理をAndroidのFragment
やActivity
のライフサイクルにバインドすることによって、そのタイミングで処理を中断させることが出来ます。
通信途中にユーザーがホーム画面に戻っても適切に終了させることができるのです。素晴らしい。
今回は以上の事を踏まえて、お天気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
に作用させるものであり、今回はRxLifecycle
のbindToLifecycle
という機能を使って居ます。
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を掘り下げた話題をちょっとしてみようと思っています。
以上、やぎにいでした!
<< RxLifecycleでのライフサイクルへのバインドについて Javaでリストを操作するときにfor文をやめてみる >>
2018やぎ小屋