見出し画像

LiveDataで一度だけ通知するEvent.ktをmulti observe対応してみた

まえがき

LiveDataを使ってイベント通知をする、いわゆるEvent stream的な実装の方法を公式で紹介しているブログがあります。

そこで紹介されている方法は2つあって、1つが SingleLiveEvent classを使う方法。そしてもう一つがEvent.ktを使う方法です。

LiveDataは常に値を持っていて、Active状態になった時に持っている値を通知します。なので画面遷移して戻ってきた場合などにも、同じ値を通知するという特徴があるのでそのままではLiveDataはEvent streamとして使えません。

このEvent.ktを使うことで再通知をしないように制御することができます。

また、ブログではSingleLiveDataよりもこのEvent.ktを使うメリットとして、複数箇所からObserveされてもバグがないことを説明してます。

で、確かに複数箇所からObserveしても予期しない行動はしないのですが、残念ながらMulti observeに対応しているわけではありません。複数Observeされている場合は最初の1つめに通知して終わりです。

つまり、このEvent.ktを使っても、SingleLiveEventを使っても、複数箇所でObserveすることはできません。たとえば、親のFragmentからDialogFragmentを呼び出し、DialogViewModelの値を複数箇所でObserveしたり、BottomNavigationのViewModelを複数の子のFragmentでObserveしたりすることはできません。

このことはEvent.ktのGitstにもたくさんReplyがついていて指摘されています。

Mutli observe可能なEvent.kt

そこでEvent.ktを使っている人が簡単にMulti observeできるように少しEvent.ktを改変してみました。

ちょっと長いのは既存のGoogleライセンス部分に自分のを追加してるからです。笑 こういうのってどうやって自分のを追加すればいいのか不明だったんでまんま下に追加しちゃってるけどいいのだろうか。?

このEvent.ktはもとのEvent.ktをそのままリプレースしても問題無い感じになってます。通常の使い方も変更なしです。

viewModel.message.observe(viewLifecycleOwner, EventObserver {
   Toast.makeText(context, it, Toast.LENGTH_SHORT).show()
}

もともとのEvent.ktとまったく同じに書けば、もともとの挙動をします。複数箇所からObserveされない場合はこのままでOKなので、すでにEvent.ktを使っているプロジェクトを破壊しないで置き換えられます。

複数箇所でObserveしたい場合はTagとして文字列を渡します。

viewModel.message.observe(viewLifecycleOwner, EventObserver("Event1") {
   Toast.makeText(context, it, Toast.LENGTH_SHORT).show()
}

viewModel.message.observe(viewLifecycleOwner, EventObserver("Event2") {
   Toast.makeText(context, it, Toast.LENGTH_SHORT).show()
}

同じmessageというLiveDataを2箇所でObserveしていますが、渡したタグ文字列が異なる場合はそれぞれで1度だけ通知するように挙動します😍

仕組みとしては、タグ文字列をハッシュマップに持っていて、同じ文字列ですでに通知済みかを判定するようにしてます。

これでいい感じにMulti observeしてくれるようになりました!!

ぜひ活用してみてください🚀

この記事が気に入ったらサポートをしてみませんか?