加筆修正しました。
私自身の理解が深まったことと、情報が増えたこと、割と見てもらってるのでなんとかしないとということで編集しました。
RxとAndroid
今ではすっかりRxjavaも言葉として定着しつつありますしプロダクトにも導入されているところが多いのではないでしょうか。
私もプロダクトに導入しつつ雰囲気で学んでしまったので整理しつつ皆さんに共有できればと思います。
RxJavaを用いる利点
今までのAndroid開発にはなかった考え方や操作だったので学習コストが高い、と言われて敬遠している人が多いかもしれませんが、今はそんなことありません。日本語資料も増えてきていますし、当たり前のように使われているのでサンプルも増えているので思い切ってチャレンジしてみるといいかもしれません。
では、そのRxjavaを用いる利点をリストアップして紹介します。
なんといっても非同期通信処理
RxJavaを使うきっかけみたいなのはここが多いのではないでしょうか。
Androidは通信処理をバックグランドで行わなければならないにも関わらずスレッドの管理が大変でした。
AsynctaskやAsynctaskLoaderなどに苦しめられた過去がある方も多いはずです。
私の経験ですが、非同期処理同士の待ち合わせ等にいじめられた過去は今でも苦い思い出です。
また、それでいて非同期通信の処理は上で上げたもの以外にも書き方がいろいろあり書く人によってバラバラの実装方法になってしまいベストプラクテイスをプロジェクト内でも統一しづらかった、などもあると思います。
Rxでは強力なオペレーター(後述)存在しているので書き方を統一するだけでなく、理解さえしていれば一瞬でコードリーディングができます。
そしてもう一つ強力な利点があります。
それはRxAndroid(RxLifecycle)を用いることでActivityやFragmentのLifecycleを意識することなく非同期通信処理が書くことができる点です。
余分なnullチェック(またはnullチェック漏れによるクラッシュ)が減るわけです。使い方は後述します。
もうこれだけでも導入、学習する価値はあると思いますがまだまだRxJavaを学ぶ利点はあります。
コレクション操作
Rxjavaの本質はコレクション操作です。
詳しくは後述しますがストリームという概念です。
for文でのコレクション操作は中身を理解するためにif文のネストを読み、パラメータが何を意味しているものなのか把握し、どのようなデータを得ているのか、ということを把握するためにすべてのコードを読まなければいけません。
しかし、RxJavaを用いたコレクション操作ではそれらの情報をすべて読む必要はありません。先ほどいったオペレーターというものがそれを明示的に示してくれます。詳しくはコレクション操作の項目でお話しします。
Viewのイベント制御
Viewのイベント制御も煩雑ですよね。書きにくいですし、冗長だしやっぱりfor文です。
よく見る例ですと、EditText等にユーザーネーム等が入力されていくのを監視して、「ダメな文字列」「文字数オーバー」などをリアルタイムで制御していくものです。
UIの動きをストリームとしてみることでこのような操作が可能になります。
詳しくはViewのイベント制御の項目でお話しします。
もう学びたくなってきたでしょ?
以上の3点が私の見出しているRxJavaを習得することで得られるメリットです。
もう学びたくなってきているでしょう?
それぞれの項目で詳しい実装はしていきますが、まずは概念的な話をふわっとしていきたいと思います。(なぜふわっとかというと私も完全な理解ができているわけではないから)
そもそもRxって何?
こうなんかすごい最近に出てきたもののように錯覚しますが、概念としてはマイクロソフトが.NET向けに提供したRX.NETが始まりです。
リアクティブプログラミングとは
根底として、リアクティブプログラミングは「オブジェクト指向プログラミング」や「関数型プログラミング」、「構造化プログラミング」などと一緒のプログラミングの基本的な枠組み(パラダイム)の一つです。
では大事な部分である「リアクティブ」とは何か。
データフローに応じて自動的に変更を伝搬することです。簡単に言えばイベントに反応するということ。
具体的な例を挙げると...
- 負荷に反応
- 障害に反応
- ユーザーの操作に反応
負荷や障害に反応してユーザーに対して常にインタラクティブなサービスを提供する
、ということです。
Androidでは通信処理はバックグラウンドで行うべきで、たいてい通信処理のあとはリスト操作であったりが行われるため相性がかなりいいわけです。
そんなリアクティブプログラミングに 関数型プログラミングの考え方を足してFunction Reactive Programingと言われる考え方。その考えに基づいた Reactive Extensionsと言われるライブラリでは非同期データストリームを行っています。
これが結果的にAndroid上で非同期処理を行っているものになります。
回りくどくなってしまいましたね。
日本語も得意じゃないですし、そこまで詳しく私も理解できていないのでこんなニュアンスだよ、という感じが伝わればいいかな、と思います。
エンジニアならコードで語ろう、ということでサンプルを用いつつ理解してもらえればいいな、と思います。
ではさっそくコード上でRxJavaを理解していきましょう。
コレクション操作をRxjavaで行う
非同期通信(http)処理でAPIを使うにしてもUIをRxbindingするにしもてコレクション操作ができないと扱いきれません。
まずはコレクション操作から学んでいきます。
Observable
何かを説明する前に、公式ドキュメントやその他解説等でよく出てくる図があると思います。色とりどりの〇とか☆とか矢印があるやつです。
あれの味方を把握してしまえば英語を読まずとも公式ドキュメントの図だけでなんとなく何をするものなのかわかったりします。
ではさっそく見ていきましょう。
まず上の棒と丸。それらすべてを「Observable」といいます。矢印の方向に向かって時間は流れていきます。矢印の終了手前にあるものはObservableの終点です。イメージとしては配列が時間に沿って流れてくる感じです。
↑の破線と↓の破線の間にある白い箱はオペレーター(Operator)と言われるものです。ここで値を整形したりなんなりいろいろします。記事にまとめてあるので慣れてきたら確認してみてください。
そして下のストリームに流します。ちなみに×は処理が失敗したことを意味します。
Observableはほかにも「イベント」である場合もあります。これがViewのイベント処理などにつながってきます。また、「非同期処理の終了」などもイベントとしてみることができるでしょう。これらはそれぞれの章でお話します。
ではさっそく、感覚をつかむためにObservalbeを生成してみましょう。
依存関係
gradleに以下を。
compile 'io.reactivex:rxandroid:1.1.0'
compile 'io.reactivex:rxjava:1.1.0'
バージョンは合わせてね。
Observableの生成
Observableの生成にもいくつか種類が存在します。よく使うものをいくつか紹介していきます。
range
range(int,int)
第一引数から第二引数までの数Observableに流します。
では実際に使ってみましょう。
Observable.range(1,10)
.subscribe(new Observer<Integer>() {
@Override
public void onCompleted() {
Log.d("onCompleted","owari");
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onNext(Integer integer) {
Log.d("onNext",String.valueOf(integer));
}
}
);
見覚えのないものが一気に増えてごめんなさい。解説しますね。
まずはObservable.range(1,10)。これは先ほど言った通り1~10のArrayのObservableを作りますよ、というもの。
それをSubscribeしています。
基本的にRXの操作ではObservableを作ってOperator(なんらかの処理をするメソッド)を通し、Subscribeでデータを受け取ります。今回の例ではOperatorが特にないため1~10のデータが順にsubscribeに流れてきます。
では実行してみてください。
10-06 13:08:16.792 2780-2780/com.example.yukin.rxjava1st D/onNext: 1
10-06 13:08:16.792 2780-2780/com.example.yukin.rxjava1st D/onNext: 2
10-06 13:08:16.792 2780-2780/com.example.yukin.rxjava1st D/onNext: 3
10-06 13:08:16.792 2780-2780/com.example.yukin.rxjava1st D/onNext: 4
10-06 13:08:16.792 2780-2780/com.example.yukin.rxjava1st D/onNext: 5
10-06 13:08:16.792 2780-2780/com.example.yukin.rxjava1st D/onNext: 6
10-06 13:08:16.792 2780-2780/com.example.yukin.rxjava1st D/onNext: 7
10-06 13:08:16.792 2780-2780/com.example.yukin.rxjava1st D/onNext: 8
10-06 13:08:16.792 2780-2780/com.example.yukin.rxjava1st D/onNext: 9
10-06 13:08:16.792 2780-2780/com.example.yukin.rxjava1st D/onNext: 10
10-06 13:08:16.793 2780-2780/com.example.yukin.rxjava1st D/onCompleted: owari
こんなログが流れてきました。
生成された1~10がonNextを通り、すべてが終わったあとにonCompletedが呼ばれているのがわかると思います。これがRxjavaの最も基本になる操作です。
ではもっとObservableを作るメソッドを見ていきましょう。
from
iteratorからObservableを生成します。
String[] items = new String[]{"sasaki","SASAKI","ささき","ササキ","佐々木"};
Observable.from(items)
コレクション操作では最も使うでしょう。
just
Observalbeを指定します。
Observable.just(1,10,4,21,19,2,13,9)
とりあえず以上の3つを使います。その他のObservableは別参照。
Operator
お待たせしました。コレクション操作においてもっとも重要なのはOperatorです。さっきから後述後述いって全然解説がないと怒っている方ももう安心してください。
ではさっそく紹介していきます。
Filter
先ほどからたびたび登場しておりますが満を持して紹介します。
Observable.just(1,10,4,21,19,2,13,9)
.filter(new Func1<Integer, Boolean>() {
@Override
public Boolean call(Integer item) {
return (item < 15);
}
})
.subscribe(new Observer<Integer>() {
@Override
public void onCompleted() {
Log.d("onCompleted","owari");
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onNext(Integer integer) {
Log.d("onNext",String.valueOf(integer));
}
}
);
中の式を評価してtrueが帰った場合に表示されます。
今回の場合は15以下がonNextで呼ばれるわけです。それ以外の場合にはonErrorが呼ばれるわけではなく、ただ単純になんのコールバックも呼ばれません。
これもよく使うものではないでしょうか。
Map
Func1でT型を受け取ってR型を返します。
意味わかります?コードを見ればわかるでしょう。。
.map(new Func1<Integer, String>() {
@Override
public String call(Integer integer) {
return String.valueOf(integer);
}
})
先ほどのObservableが流れてくるとして、
1 -> "1"
10 -> "10"
といった変換が行われているものだと思ってくれればいいです。
あまり使いませんが類似品を紹介します。
- cast
- encode
- byLine
です。気になったら調べてみてください。
コレクション操作でもっとも使うのはこのあたりではないでしょうか。
その他たくさんOperatorがあります。こちらも合わせてお読みください
でもこれだけ知っておけばまず間違いなくコレクション操作は行えるでしょう。
ぜひRx生活を満喫してください。