直感的に理解する RxJava その3: リアクティブであることのメリット

ReactiveX

RxJava について, その1では「基本的な構造」, その2では「強力な Operator」についてみてきました. しかし, あなたは RxJava のメリットについて納得していないでしょう. ここでは, RxJava フレームワークがもつ他のいくつかのメリットを明らかにしていきます.

エラーハンドリング

ここまで onComplete() と onError() には触れませんでしたが, これらは, Observable がアイテムを発するのを停止したときに, 成功完了なのか, エラー停止したのかを示しています.

Subscriber は onComplete() と onError() をリッスンする機能をもっています. 実際にやってみましょう.


Observable.just("Hello, world!")
    .map(s -> potentialException(s))
    .map(s -> anotherPotentialException(s))
    .subscribe(new Subscriber<String>() {
        @Override
        public void onNext(String s) { System.out.println(s); }

        @Override
        public void onCompleted() { System.out.println("Completed!"); }

        @Override
        public void onError(Throwable e) { System.out.println("Ouch!"); }
    });

potentialException() と anotherPotentialException() の両方が, Exceptionを throw する可能性があるとしましょう.

すべての Observable は一回のコールにつき onComplete() か onError() で終了します. そのとき, プログラム出力は "Completed!" かまたは, Exception が throw されたときは "Ouch!" を 最後に出力します.

これらから分かることがあります.

1.「Exception が throw されたときはいつでも onError() がコールされる」

このことは, エラーハンドリングを極めてシンプルに可能にすることを意味しています. 結果, これだけですべてのエラーをハンドルできます.

2.「Operator は Exception をハンドルする必要がない」

Exception は onError() までスキップするので Observable チェインのどこかで起きた問題をどのように処理するかを Subscriber までは気にしなくて良いということになります.

3.「Subscriber がアイテムの受取りを完了したかどうか把握できる」

タスクが終了するタイミングを知ることはコード上で必要になることがあります. ただ, Observable が終了しない可能性もあります.

このことは, これまでのエラーハンドリングに比べてかなり分かりやすいでしょう. コールバックを使う場合は, それぞれのコールバックでエラーをハンドルしなければならず, 同じコードの繰り返すことになりますし, それぞれのコールバックは, どのようにエラーをハンドルするか, を知らなければなりません. このことは, コールバック部分のコードは密に呼び出し側と連携されているということになります.

RxJava では, Observable は エラー時の処理を知る必要が無く, エラーの状態をハンドルする必要もありません. エラーの場合は処理をスキップして, すべてのエラーハンドリングを Subscriber に置くことができます.

Scheduler

Androidアプリでネットワークリクエストを利用するとします. それが時間がかかるものであれば, 別のスレッドでロードします. そこで問題がおきたりすることが有ります.

マルチスレッドのAndroidアプリは難しく, 正しいスレッドで正しいコードを実行することを確実に行わなければなりません. これは混乱してアプリがクラッシュする可能性がとなります. メインスレッド以外からViewを操作するとException が発生することはご存知だと思います.

RxJava では, subscribeOn() を使うことで どのスレッド上で実行するかを Observer に教えることができます. また observeOn() を使うことで Subscriber を実行するスレッドを指定できます.


myObservableServices.retrieveImage(url)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(bitmap -> myImageView.setImageBitmap(bitmap));

シンプルですね. Subscriber までのすべての処理は I/Oスレッドで実行されます. そして最後にメインスレッドでViewの操作が行われます.

メインスレッドのブロックをできるだけ少なくするために, Subscriber はできるだけ軽くする.

これらは非常に便利で, すべての Observable に subscribeOn() と ObserveOn() を追加することができます. ただの Operator です. 何の Observable であるか, 前の Operator が何をしているかについては気にする必要はありません. 簡単にスレッドの振り分けを指示することができます.

Observable の処理に時間がかかる場合に, Subscriber がすでにI/Oスレッドで準備できていれば, それを監視する必要がないので subscribeOn() から observeOn() を先延ばしにすることは良い実験になります.

AsyncTask などでは, どの部分を並列処理にするかある程度設計しなければなりませんでしたが, RxJavaを使うと そのまま並列処理と追加するだけでよいのです.

Subscription

他にもまだ説明していないメリットがあります. Observable.subscribe() をコールした時これは Subscription を返します. これは, Observable と Subscriber のリンクです.


Subscription subscription = Observable.just("Hello, World!")
    .subscribe(s -> System.out.println(s));

このリンクである Subscription を以下のように利用することができます.


subscription.unsubscribe();
System.out.println("Unsubscribed=" + subscription.isUnsubscribed());
// "Unsubscribed=true" と出力される

RxJava が unsubscribe を操作することの良いところは, チェインをストップすることができることです. 複雑な Operator のチェインを利用している場合, unsubscribe を使うことでいつでも実行中のコードを停止することができます. 他に処理は必要ありません.

その1で Observable.just() は, onNext() と onComplete() を単純にコールすることに比べて, すこし複雑であることを書きました. その理由は Subscription にあります. それは onNext() をコールする前に, Subscriber がすでに subscribe されているかどうかを実際にチェックするからです.

まとめ

気に止めておいて欲しいのは, これらの話は RxJava の序論です. ここで説明したことよりもっと多くの学ぶべきことがあると思います. たとえば, Backpressure について調べてみてください.

すべてに これらのリアクティブなコードは使う必要はありません. シンプルなロジックにしたい複雑なコードに対して利用しましょう.

これらの説明で このおもしろいフレームワークを始めるには十分だと思います. もっと学びたければ, 公式 RxJava Wiki を読むことをおすすめします. そして「可能性は無限」であることを忘れないでください.

元々は, この記事で最終にする予定でしたが Android 向けの実用的なサンプルのリクエストが多く, その4 を書いています.

[原文] Grokking RxJava, Part 3: Reactive with Benefits

直感的に理解する RxJava その1: 基本的な構成

直感的に理解する RxJava その2: Operator

直感的に理解する RxJava その4: Reactive Android


Observable 複数データソース に優先順位をつける

通信を使うとき, 必要になるだろうよくあるパターンだと思います.

通信状況や不要なリクエストやキャッシュを使うかどうか.

どこからデータを取得してくるか.

- メモリ
- ストレージ
- ネットワーク

それぞれのソース別に Observable を作ったら, 優先順位をつけたら連結する.

ReactiveX_-_Concat_operator

ReactiveX - Concat operator

条件をつけて, 最初の一つだけ利用する.

ReactiveX_-_First_operator

ReactiveX - First operator

そんなサンプルコードがあります.


// Create our sequence for querying best available data
Observable<Data> source = Observable.concat(
    sources.memory(),
    sources.disk(),
    sources.network()
  )
  .first(data -> data != null && data.isUpToDate());

// "Request" latest data once a second
Observable.interval(1, TimeUnit.SECONDS)
  .flatMap(__ -> source)
  .subscribe(data -> System.out.println("Received: " + data.value));

rxjava-multiple-sources-sample/Sample.java


public Observable<GfyItem> getGfyItem(final String gifUrl, final String gfyName) {

  // Use the first source that returns a valid name
  return Observable.concat(

    // We already have the name
    Observable.just(gfyName),

    // We check for a pre-converted gif (for the gfyname)
    getPreExistingGfyName(gifUrl),

    // We need to convert the gif (then retrieve the gfyname)
    convertGifToGfyName(gifUrl)
  )
  .first(result -> !TextUtils.isEmpty(result))
  .flatMap(this::getMetadata)
  .map(GfyMetadata::getGfyItem);
}

android-gfycat/GfycatService.java

非常にわかりやすいサンプルや記事で勉強になりますっ.

Loading data from multiple sources with RxJava


Swift が Android 上で動き始めてよく分かる Kotlin の素晴らしさ

Swift がAndroid上で動き始めており,

_RFC__Port_to_Android_by_modocache_·_Pull_Request__1442_·_apple_swift

[RFC] Port to Android by modocache · Pull Request #1442 · apple/swift

Hacker News でも少し話題になっていたので眺めておりました.

Swift_Ported_to_Android___Hacker_News

Swift Ported to Android | Hacker News

「Swift が Android上で動くこと」について話題がされているかと思いきや Kotlin と比較され, Kotlin のAndroid上で動かすことの良さがはっきりと分かるスレになっておりました.

Surprised noone mentions Kotlin. It's quite Swift-like, backed by JetBrains (Android Studio is based on their IntelliJ Idea), and 1.0 has only just been released. It has full interoperability with Java.

Kotlin に誰も言及していないのが驚き. Kotlin は Swift によくにており, JetBrains (AndroidStidioのベースとなるIntelliJ Idea の開発元)がバックアップしており, 1.0 がリリースされたばかりのもので Java と完全に相互連携ができる.

I love Kotlin, but being able to develop libraries in one language and use them in both Android and iOS is huge. I have been using J2Objc for this until now, and while it's a great tool, it forces me to use Java, which I don't love. I would prefer being able to use Kotlin on iOS, but using Swift for Android development is a great boon.

私はKotlinが好きだが, ひとつの言語でAndroidとiOSで開発ライブラリを利用可能にすることは大変だ. いままで J2Ojc を使っておりこれは素晴らしいツールでJavaを使うことを強いられるが私は好きではない. それより Kotlin を iOS上で利用可能にするほうがいい. Android開発にSwiftを使うのは 非常にありがたいのだが...

Kotlin emits bytecode. It is entirely interoperable with Java. All the Java APIs of the platform are accessible in Kotlin.
Swift on the other end can only target the NDK, which limits it to a very specific niche on Android.

Kotlin はバイトコードを吐く, これは Java と完全に相互連携できること. すべてのJavaAPI はKotlin で利用できる.
一方 Swift は Android上では NDKのみに限定され非常に狭い.

What helps a lot is that the kotlin team has written many helper methods allowing a better flow between the android API and kotlin code : while you don't need it in order to get interop, it allows to more easily write idiomatic kotlin code while interacting with Android.

Kotlin チームの書いたたくさんのヘルパーメソッドは, Android API と Kotlin 間の流れをより良くしており, Swift ユーザがそれを利用しないことと対照的に, Androidと連携しながらより簡単に慣用的な Kotlin コードを書くことができる.

The main problem with Swift on Android is that AFAIK it is going to be limited to NDK.
There is certainly a niche where it can be useful, but for most developers, it makes it a no go.
Kotlin on the other end is indeed a very good stand-in replacement for java on Android.

私の知る限りSwift を Android上で動かすことの大きな問題は NDKに限定されることだ. このことは確実に便利さを狭めておりそれが進行を妨げている. 一方, Kotlin は確かに Android 上での Java の代替となる.

No one cares if you don't use Swift. Go with Kotlin. It's probably a great choice for you. It seems like a great language and I hope it gains traction.

Swift は使わなくて良い. Kotlin でいこう. あなたにとって素晴らしい選択となる. 偉大な言語で勢いを増すことを願っている.

If all you're doing is Android programming Kotlin is probably a better choice at this time. If you're a Java programmer, Kotlin is probably a better choice. If you're already a Swift programmer...
Also, let's see how both languages gain traction in the next 3-5 years.

Androidプログラミングのみを行っていたり, Javaプログラマーであるなら Kotlin はおそらくより良い選択です. もしあなたがすでにSwiftプログラマーであるなら...
次の3-5年でそれぞれがどのように勢いを増すかを見ておきましょう.

しかし,「ことりん」て名前. かわいいよなあ.

あの「Hacker News」で ベストなストーリーを見つける方法