LiveDataをイベントの通知(トーストやダイアログを表示指示)として利用する、いわゆるLiveEventの実装をまとめました。
どれを使えばいいの?
結論から先に言うと、hadilq/LiveEventまたは最初の値を無視を使うといいと思います。その他に紹介しているSingleLiveEvent、Event、SingleLiveEvent2は実装に問題があるので使わないほうがいいです。
SingleLiveEvent
architecture-samplesのTODOアプリで昔使われていたやつ。MutableLiveDataを継承しsetValue()等で設定された値は一度だけonChanged()に流れることを保証するクラス。
問題点
複数Observerが存在する場合に、一つのObserverだけにしか値が流れない。
Event
SingleLiveEventの改良。MutableLiveDataを継承した独自クラスではなく、通常のLiveDataにEventでラップした値を流す。
https://github.com/android/architecture-samples/blob/todo-mvvm-live-kotlin/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/Event.kt
https://medium.com/androiddevelopers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150
改善点
一度だけ値を取得できるgetContentIfNotHandled()と常に値を取得できるpeekContent()の2種類のインターフェースを提供することで、Viewにイベントを通知しつつLoggerで毎回値を取得するようなユースケースは達成できるようになった。
問題点
SingleLiveEventと同じく、イベントとして購読したいObserverが2つ以上ある場合には利用できない。
値をラップしないと使えないので若干冗長。
SingleLiveEvent2
SingleLiveEventの改良。MutableLiveDataを継承した独自クラスでObserverのMutableSetを管理し、全てのObserverに対して一度だけonChanged()が呼ばれるようにする。
改善点
Eventで実現できない複数Observerに対するイベントの通知を実現。
問題点
最初に購読したLifecycleOwnerのライフサイクルに、その後購読した全てのObserverが従ってしまう。
最初の値を無視
LiveDataでイベントを扱った時に起こる問題は最新の値がキャッシュされることに起因するので、それを排除するというアプローチ。LiveDataとobserve()の両方を拡張関数で手を入れることでキャッシュされる値を無視する。
利点
シンプルな拡張でイベントの通知を実現。
問題点
LiveDataとobserve()の両方が正しく揃うことで動作するので、他の実装に比べてバグが混入しやすい。
hadilq/LiveEvent
MediatorLiveDataを継承した独自クラスの内部でObserverをラップするObserverWrapperを生成、その中で一度だけのイベント通知を実現している。
利点
ライブラリとして簡単に利用できる。
問題点
無さそうに思える。
まとめ
| 手法 | 複数Observer対応 | イベントが発生した後の購読で値が流れない |
|---|---|---|
| SingleLiveEvent | × | × |
| Event | △ | × |
| SingleLiveEvent2 | ○ | ○ |
| 最初の値を無視 | ○ | ○ |
| hadilq/LiveEvent | ○ | ○ |