RxJavaDay 7

RxJavaで安全にイベント通知を行うためのRxRelay

More than 1 year has passed since last update.

これは RxJava Advent Calendar 2017 の7日目の記事です。意外とカレンダーがスカスカしていてRxJava自体の知見はだいぶ出尽くしてしまったのかなという感じですね。

今回はRxRelayというライブラリを紹介しようと思います。

※ RxJavaについての基礎知識はあることを前提とした記事です。


RxRelayとは


  • https://github.com/JakeWharton/RxRelay

  • Android界隈では神とも呼ばれる、Jake Wharton氏によるRxJava用のライブラリ

  • Subjectより安全な、Relayという仕組みを導入するもの

RelayはSubjectから onCompleteonError を呼ぶ機能をなくしたもので、つまり onNext で値を流すことに特化したSubjectということができます。Relayは以下の性質・機能を持っています。



  • Observable および Consumer としてふるまう


  • accept で受け取った値を onNext に流す

  • ストリームが終了することはない


  • observable.subscribe(relay) はDisposableを返すので購読の解除がかんたん


  • toSerialized() を呼ぶことによりスレッドセーフなRelayを生成できる


  • AsyncSubject にあたる AsyncRelay は存在しない(ストリームの終了がないため)

これらの性質により、Relayは非RxなAPIをRx用のイベントストリームに変換するのに非常に適しています。


Relayの使いどころ

Subjectを使っていて onNext で値を流しているだけのものはRelayに置き換えてしまうことが可能です。

例えば、RxBindingを使ってMVVMのデータバインディング(AndroidのDataBinding Libraryではないです)を実装しているパターンで、ボタンのクリックイベントをViewModelに伝えたい場合、Subjectで書くと以下のようになります。

class SomeViewModel {

val onButtonClick = PublishSubject.create<Unit>()
}

val viewModel = SomeViewModel()

button.clicks()
.subscribe { viewModel.onButtonClick.onNext(Unit) }
.addTo(...)

これをRelayで置き換えると、

class SomeViewModel {

val onButtonClick = PublishRelay.create<Unit>()
}

val viewModel = SomeViewModel()

button.clicks()
.subscribe(viewModel.onButtonClick)
.addTo(...)

このようにスッキリ書くことができ、 onNext が排除されてデータバインディングのリアクティブ感もより高まると思います。(個人の感想です)


RxSwiftにも輸入された

RxSwiftにもRxSwift4からRelayが導入されたようです。正確には、Cocoa/UIKitとのバインディングを提供するRxCocoaの方に導入されたのですが、ほぼ標準の機能と言って良いでしょう。

参考記事:

http://tech.mercari.com/entry/2017/12/04/103247

http://tech.connehito.com/entry/variable-is-deprecated

RxJava/RxSwiftで両OS用アプリを開発している身としては非常に助かります :smiley:

RxJavaもRelayを標準機能として取り入れてもいい気がしました。


追記: RxJSにも

ちょうどこの記事を書いた数日後ですが、RxJS向けのRelayもリリースされていました。(しかもMicrosoft製)

https://github.com/Microsoft/RxRelayJS

以上です。