ユースケースで入門するAndroid RxJava1

  • 51
    いいね
  • 4
    コメント

RxJavaの入門記事でリスト操作や非同期処理が楽になるってのはわかったんだけど、それだけだとイマイチRxJavaの凄さが伝わってこないなぁという人向け

Observableってなんだって人はまずはこちらを一読するといいかもしれません。
みんからきりまで - RxAndroidをカジュアルに使ってみるとか

Observableを合成して複雑なイベント処理を簡潔にする的なユースケースを紹介します。
RxJava以外にRetrofit, RxBinding, RxAndroidというライブラリもつかってますが、特に気にしなくて大丈夫でしょう。

複数APIリクエスト

複数のAPI待ち合わせ

2つのAPIからStringを取得して、レスポンスが揃ったらそれぞれをTextViewに表示してみましょう。

kotlin
Observable.zip(fooClient.fetch(), barClient.fetch()) { fooResponse:String, barResponse: String -> Pair(fooResponse, barResponse)}
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(
        { pair ->
            fooTextView.text = pair.first
            barTextView.text = pair.second
        },
        { Log.e("TAG", it) }
    )

zipは第一引数と第二引数にObservableをとります、第三引数は両Observableで対となるNextシグナルが発行されたときに実行される関数を取ります。
第三引数に渡した関数は第一引数と第二引数に両Observableのアイテムをとります。返り値は任意です。この場合、fooResponseとbarResponseのPairを生成して返しています。すると、この関数で返したPairがzipで合成されたObservableのNextシグナルとしてエミットされます。

複数のAPIを逐次実行

fooClient.fetch()
    .subscribeOn(Schedulers.io())
    .flatMap { fooResponse ->
        fooTextView.text = fooResponse
        barClient.fetch()
    }
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(
        { barResponse -> barTextView.text = barResponse },
        { Log.e("TAG", it) }
    )

flatMapを利用することで逐次的にAPIを実行することができます。
まずFooAPIを叩き、レスポンス掛かってきてfooResponseがエミットされるとflatMapに渡した関数が実行されます。
flatMapに渡した関数はflatMapを適用したObservableのNextシグナルを引数に取り、任意のObservableを返すことができます。ここではflatMap内でfooTextViewへの表示を行い、BarAPIを叩くObservableを返しています。

入力フォームバリデーション

「チェックAがONならば、項目Bは入力必須とする」という Validation を RxJava + RxAndroid でやる
こちらと同様です。
RxAndroidが複数のライブラリに分割されたので、RxBindを利用した場合について書きたいと思います。

ezgif.com-video-to-gif.gif

  • 何も入力されていない場合ボタンがDisableになり、住所が入力されたらボタンがEnableになります。
  • 別の住所を利用するにチェックが入いるとその入力欄がEnableになります。
  • チェックが入っていて別住所が未入力であればボタンがDisableになります、入力されればEnableになります。

RxJavaとViewのバインドにRxBindingを利用して処理を書くと以下のようになります。

val addressObs = RxTextView.textChanges(addressEditText)
val secondAddressObs = RxTextView.textChanges(secondAddressObs)
val checkBoxObs = RxCompoundButton.checkedChanges(checkBox)

val secondAddressValidation = Observable.combineLatest(checkBoxObs, secondAddressObs) { isChecked, secondAddress ->
    val isValid = if (!isChecked) {
        secondAddressEditText.enabled = false
        true
    } else {
        secondAddressEditText.enabled = true
        if(secondAddress.isNotEmpty()) true else false
    }
    return@combineLatest isValid
}

Observable.combineLatest(secondAddressValidation, addressObs) { isSecondAddressValid, address ->
    return@combineLatest if (isSecondAddressValid && address.isNotEmpty) true else false
}.subscribe { isValid -> button.enabled = isValid }

Observable#combineLatesは合成したobservableのうちどれかに値が入ってきたら、その値と他のobservableの直前の値を流してくれるobservableをつくってくれます。
合成したobservableのどれかで状態が変化したらその時点でのすべてのobservableの状態を流してくれるってことですね。