Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
イベントをどう管理するか
Search
mikan
December 06, 2024
Technology
3
310
イベントをどう管理するか
【祝!50回】Shibuya.apk #50
https://shibuya-apk.connpass.com/event/336398/
mikan
December 06, 2024
Tweet
Share
More Decks by mikan
See All by mikan
RepositoryのSSoT化
mikanichinose
0
30
Kotlin Multiplatform 始めました
mikanichinose
1
120
Web APIをなぜつくるのか
mikanichinose
0
1.9k
ライブラリでしかお目にかかれない珍しい実装
mikanichinose
2
410
Strong Skipping Mode によってrecompositionはどう変わったのか
mikanichinose
0
260
Modeling UiEvent
mikanichinose
0
60
UIの構成要素に関する考察
mikanichinose
0
59
再考: 監視可能オブジェクト
mikanichinose
0
73
マルチモジュール懐疑派だったかつての自分に送る マルチモジュールの効能
mikanichinose
0
220
Other Decks in Technology
See All in Technology
Cline、めっちゃ便利、お金が飛ぶ💸
iwamot
20
19k
7,000名規模の 人材サービス企業における プロダクト戦略・戦術と課題 / Product strategy, tactics and challenges for a 7,000-employee staffing company
techtekt
0
110
AIエージェントキャッチアップと論文リサーチ
os1ma
6
1.3k
AWS CDK コントリビュート はじめの一歩
yendoooo
1
130
開発現場とセキュリティ担当をつなぐ脅威モデリング
cloudace
0
130
30 代子育て SRE が考える SRE ナレッジマネジメントの現在と将来
kworkdev
PRO
0
170
20250326_管理ツールの権限管理で改善したこと
sasata299
1
580
アプリケーション固有の「ロジックの脆弱性」を防ぐ開発者のためのセキュリティ観点
flatt_security
38
15k
コンソールで学ぶ!AWS CodePipelineの機能とオプション
umekou
3
130
TopAppBar Composableをカスタムする
hunachi
0
170
Symfony in 2025: Scaling to 0
fabpot
2
270
Startups On Rails 2025 @ Tropical on Rails
irinanazarova
0
160
Featured
See All Featured
Optimising Largest Contentful Paint
csswizardry
35
3.2k
YesSQL, Process and Tooling at Scale
rocio
172
14k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
7
620
How STYLIGHT went responsive
nonsquared
99
5.4k
The Pragmatic Product Professional
lauravandoore
33
6.5k
Done Done
chrislema
183
16k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
102
19k
GraphQLとの向き合い方2022年版
quramy
45
14k
Code Review Best Practice
trishagee
67
18k
Statistics for Hackers
jakevdp
798
220k
Raft: Consensus for Rubyists
vanstee
137
6.9k
Large-scale JavaScript Application Architecture
addyosmani
511
110k
Transcript
イベントをどう管理するか Shibuya.apk #50 mikan(一瀬喜弘)
自己紹介 object Mikan { val name = 一瀬喜弘 val from
= 長崎 val company = カラビナテクノロジー株式会社 val work = Engineer.Android val volunteer = DroidKaigi val hobby = listOf( "漫画", "アニメ", "ゲーム", "折り紙", "OSS開発・コントリビュート", ) }
今日お話しする内容
Channel vs. SharedFlow vs. StateFlow エラーや 通信結果などの イベントを どこで 管理するのか
結論 エラーや 通信結果などの イベントを どこで 管理すべきか?
それぞれの メリデメを 把握したうえで 適当な ものを 選んでください
どれを 使うかは 所詮手段でしかないです 取り扱おうと している イベントの 重要度だったり、 どう ハンドリングされるかを きちんと
把握しないと 適切な ホルダーは 選べません
これから メリデメを 紹介するので、 適した 手段を 検討できるようになって もらえれば 幸いです
イベントとは
イベントとは ユーザー操作によってUI側から発生し、ViewModelに 伝達したい情報
None
けど ViewModelから UIに イベントを 送信したい ときも ありますよね
ex. 1. 通信が完了したら画面遷移する 2. 通信が失敗したらエラーダイアログを表示する 3. SharedPreferencesからデータを削除したらトーストを表示す る
None
ViewModelから UIに イベントを 伝達する ためにはなにかしら データホルダーが 必要に なる
Channel? SharedFlow? StateFlow? 戦争が始まる
それぞれについて実装方法をみ ていきます
Channel データの持ち方 イベントの発火 // ViewModel private val _message = Channel<String>()
val message = _message.receiveAsFlow() viewModelScope.launch { _message.send("message...") }
Channel イベントの消費 // Fragment viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.message.collect {
showSnackBar(it) } } }
Channel イベントの消費 // Compose val lifecycle = LocalLifecycleOwner.current.lifecycle LaunchedEffect(Unit) {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.message.collect { showSnackBar(it) } } }
Channel メリット collectorがいない間に発生したイベントをバッファリングできる(demo) デメリット イベントの 消費中に coroutine が キャンセルされたら どうしますか?
(demo)
Channel メリット collectorがいない間に発生したイベントをバッファリングできる(demo) デメリット イベントの 消費中に coroutine が キャンセルされたら どうしますか?
(demo) → バッファリングの挙動とバックグラウンド時のキャンセルが絡み合うとイベン トハンドリングの振る舞いがどんどん予測困難になる
SharedFlow データの持ち方 イベントの発火 // ViewModel private val _message = MutableSharedFlow<String>()
val message = _message.asSharedFlow() viewModelScope.launch { _message.emit("message...") }
SharedFlow イベントの消費 Channelのときと同じ // Fragment viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.message.collect
{ showSnackBar(it) } } }
SharedFlow イベントの消費 Channelのときと同じ // Compose val lifecycle = LocalLifecycleOwner.current.lifecycle LaunchedEffect(Unit)
{ lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.message.collect { showSnackBar(it) } } }
SharedFlow メリット デメリットに目を瞑れば、シンプルにイベントを実装できる デメリット collectorがいないとイベントは垂れ流されてロストする(demo) ex. 構成変更が発生するとUIが再構成されるので一瞬collectorがいなくなっ て、その間に発生したイベントはロストする イベントを発火するときに、subscriptionCountが1以上になっていること をチェックすることで回避可能だが。
。 replayCacheをいくらか設けて回避するという手もある
SharedFlow メリットともデメリットとも取れる collectorが複数いた場合に、1つのイベントが同時に複数回消費されることに なる(demo) ハンドリングが同じ場合は期待しない挙動になる可能性がある ハンドリングが異なる場合は有用な可能性がある このことを考慮して設計してあるならヨシ → Channelだと複数のcollectorに同じ情報を伝達できない
StateFlow データの持ち方 イベントの発火 // ViewModel // 初期値が必要だがイベントに初期値などないのでnullで表現する private val _message
= MutableStateFlow<String?>(null) val message = _message.asStateFlow() _message.value = "message..."
StateFlow // Fragment viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.message.collect { if
(it != null) { showSnackBar(it) // イベントをハンドリングしたあとは明示的に消費する viewModel.consumeMessage() } } } } // ViewModel fun consumeMessage() { _message.value = null }
StateFlow // Compose val message by viewModel.message.collectAsStateWithLifecycle() LaunchedEffect(message) { message?.let
{ onMessageReceive(it) viewModel.consumeMessage() } }
StateFlow メリット イベント処理中にcollectorがキャンセルされてたとしても、復帰後に最新のイ ベントは残っているので再ハンドリング可能 デメリット イベントの消費をプログラミングしないといけない 明示的にイベントを消し込み イベントを消費したとは? 論理的には消費したが、物理的に消費してない状態が成立する(demo)
まとめ Channel ⭕ トーストにメッセージを表示するなどの単純なイベントに向いている ⭕ collect前にイベントが発火する可能性がある場合に向いている ❌ イベントが消失する可能性があるので、重要なイベントには向いてない SharedFlow ⭕
トーストにメッセージを表示するなどの単純なイベントに向いている ⭕ バッファリングとか考えなくてよいのでシンプル ❌ イベントが消失する可能性があるので、重要なイベントには向いてない StateFlow ❌ 単純なイベントについては過度 ⭕ イベントの処理を保証しないといけない場合は向いている ⭕ 重要なイベントに向いている
ご清聴ありがとうございました