LiveData vs Flow vs RxJava
Kotlin Coroutinesの進化はすざましく、とどまるところを知りません。
状態やイベントを扱いやすくなったStateFlowやSharedFlowが登場し、さらには、Lifecycleをより扱いやすくなったLifecycleOwner.addRepeatingJobやLifecycleOwner.repeatOnLifecycle、Flow.flowWithLifecycleが追加されました。
RoomやDataStore等Jetpackの各種ライブラリでもCoroutinesが使われており、Jetpack Composeでも様々なところでCoroutinesが活用されています。
そういった中で、一部LiveDataやRxJavaからCoroutinesに書き直す動きが見られ、多少混乱を招いていると感じています。
結論から言うと、現時点において積極的にCoroutinesに移行する必要性はないと考えています。
今回は、それらを比較しつつ、どういった使い分けをするのが好ましいのか私の視点からまとめます。
各々の特徴
軽く特徴を紹介します。
LiveData
LiveDataはJetpackライブラリ群の一つです。
Lifecycle を考慮したObservableであり、onStop時に自動で購読を解除してくれ、onStart時に再開をしてくれます。
値を保持することに特化しており、購読時に最新の値を通知してくれます。
そのため、イベント通知にはそのまま使うことは出来ません。
Flow
FlowはKotlin Coroutinesにおいて値を複数回送信することができるInterfaceです。
状態を表現するためのStateFlowや、イベント通知のためのSharedFlowが存在します。
Kotlin Coroutinesのsuspend functionやChannelとも当然相性が良いです。
RxJava
RxJavaはReactiveXのJava実装です。
ReactiveXはStreamを用いて非同期かつイベントベースのプログラミングを実現するためのライブラリです。
様々な言語で利用可能になっています。
Kotlinへの拡張を持ったRxKotlinも提供されています。
比較
それぞれを各観点から比較します。
Lifecycle
非同期処理をAndroidアプリ開発で扱うにおいて、困難なポイントにLifecycleがあります。
安易に非同期処理を扱うと、onDestory以降でViewを参照してクラッシュしたり、バックグラウンドになっているのにも関わらず不要にリソースを消費する可能性があります。
LiveData は強力にLifecycleをサポートします。
Flowに関してはlivedata-ktxにてlaunchWhenStarted等のLifecycleに従うツールが提供されています。
しかし、launchWhenStartedをFlowに対して使う際は、バッファに関して注意が必要です。
それの課題を解決し、より扱いやすくしたLifecycleOwner.addRepeatingJob、LifecycleOwner.repeatOnLifecycle、Flow.flowWithLifecycleが登場しましたが、まだα版での提供になっています。
2021-04-06 追記
RxJavaのLifecycleの対応はRxLifecycle等のサードパーティのツールを使うことで行うことが出来ます。
DataBinding
DataBindingはxmlから値を参照することで、コード量を削減することが出来ます。
LiveDataはDataBindingに対応しています。
FlowはStateFlowを使うことでDataBindingで扱うことが出来ますが、まだCanary版のみになっています。
RxJavaはDataBindingに対応しておらず、ObservableFieldかLiveDataに変換する必要があります。
Javaからの利用
LiveDataとRxJavaはJavaから利用が可能です。
FlowはKotlinからのみ利用ができ、Javaからは使うことは出来ません。
Null安全
FlowはKotlinで書かれているため、完全にNull安全です。
また、RxJavaもNull安全で扱えます。
LiveDataはLintでチェックできるようになりましたが、完全にNull安全と言えるかというと、少し難しいところがありそうです。
マルチプラットフォーム
FlowはKotlin Multiplatformに対応しており、AndroidとiOSのコードを一部共通化することが可能です。
RxJavaはReactiveXに準拠したRxSwiftやRxDartが存在し、複数プラットフォームで似たような記述が可能です。
安定性
Flowはまだまだ成長途中のため、今後破壊的変更や大幅な機能追加も考えられます。
RxJavaやLiveDataはかなり安定してきていると考えられるでしょう。
複雑性
RxJavaはオペレータの数が膨大で、かなり複雑に感じます。
FlowはRxJavaと似ている部分も多いですが、一度のみ値を返すsuspend functionは非常にシンプルで、比較するとRxJavaより洗練されてると言えるでしょう。
LiveDataは複雑な機能は殆どないので、一番シンプルだと思います。
一方で、機能の豊富さという観点から見ると、逆の結果になるでしょう。
外部ツールの連携
LiveDataはJetpackの各種ライブラリと相性が良く、必要なことはほぼほぼできると思います。
FlowはJetpackライブラリ群でも力を入れて支援をしています。
まだまだ発展途上ですが、今後より使いやすくなっていくことでしょう。
RxJavaは周辺ライブラリの開発も活発で、多くのツールが存在します。
一方で、Google公式によるサポートはあまり無いように感じます。
また、Kotlin Coroutinesの発展とともに、最近は下火になっている印象があります。
Jetpack Composeとの相性
LiveData、Flow、RxJavaともにJetpack ComposeのStateに変換可能なオペレータが用意されています。
どれを利用していても、Jetpack Composeは利用可能です。
一方で、Coroutinesに対応したrememberCoroutineScopeやLaunchedEffect等のAPIが存在し、若干Coroutinesに軍配が上がりそうです。
比較一覧
上記を表にすると、以下のようになるでしょう
| LiveData | Flow | RxJava | |
|---|---|---|---|
| Lifecycle | ◎ | ◯ | ◯ |
| DataBinding | ◎ | ◯ | ✕ |
| Javaからの利用 | ◎ | ✕ | ◎ |
| Null安全 | △ | ◎ | ◎ |
| マルチプラットフォーム | ✕ | ◯ | ◯ |
| 安定性 | ◯ | △ | ◯ |
| 複雑性 | ◎ | ◯ | △ |
| 機能の豊富さ | △ | ◯ | ◎ |
| 外部ツールとの連携 | ◯ | ◯ | ◯ |
| Jetpack Composeとの相性 | ◯ | ◎ | ◯ |
使い分け
様々な観点から3つを比較してきましたが、一長一短といったところで、今の所これが良い、これは悪い、みたいなところは無いと思います。
それぞれの長所、短所を理解した上で、自分のプロジェクトにあったツールを採用することが大事です。
強いて言うのであれば、やはりFlowは今後より発展が見込めるため、新規プロジェクトで採用したり、LiveDataやRxJavaから徐々に移行していくことは悪い選択肢ではないと思います。
データレイヤーでの取り扱いや、イベント通知等を含め、統一感のあるコードを書くことが可能になります。
一方で、α版でのみ提供されている機能があったり、今後大きな変更がある可能性を考えると、多少慎重になる必要があるでしょう。
また、個人的にLiveDataのシンプルさは好きです。
シンプルなプロジェクトであれば、新規プロジェクトであってもLiveDataを中心に組むと良いと思います。
RxJavaは複数プラットフォームで似た書き方ができるという大きな特徴があります。
ReactiveXの思想でコーディングするためには、RxJavaが必須です。
冒頭にも言いましたが、現段階においてCoroutinesに積極的に置き換える理由はないでしょう。
複数の書き方が入り交じるプロジェクトは、理解が困難になり、不具合も入り込みやすくなります。
引き続きLiveData、RxJavaを使い続けることは、それもまた非常に良い選択です。
選択肢が多いと悩みがちですが、各々にとって一番使いやすいツールが見つかることを祈っています。
Kotlin Coroutinesの解説本をZennにて販売しています。より詳しく学びたい方は、こちらも合わせて確認してみて下さい。
