LaunchedEffect で何か処理をするコードはよく見るけど、例えばこういうのがあったとする。
@Composable
fun MyScreen() {
LaunchedEffect(Unit) {
}
}
Screen としている Composable 直下に処理がある。つまり、この画面を表示した時、一度だけ何か処理をしたい、ということなのだろう。
画面回転や、ナビゲーションでこの画面に戻ってきた時も動く、という処理になる。細かいけど画面分割していたのなら画面サイズを変えてももちろん動く。
かつてはこれでも問題なかったケースが多かったかもしれない。画面サイズの変更などは、特に頻発せず重要ではない、と考えているなど。
ただ Predictive Back が有効の時、戻るジェスチャという頻出するケースで影響が出ることになる。
Predictive Back が有効の時、戻るジェスチャ開始したあたりで LaunchedEffect が書かれたこのコードは入場する。つまり、まだ戻る動作が完了していない状態で LaunchedEffect が動いてしまうという問題が出てくる。
例えばここに通信処理を書いていたら、まだ戻っていないのに発火して動く。Karte で View イベントを送信するぞ!とか。Predictive Back はジェスチャにより戻る操作をキャンセルもできるので、タイミングが少し速くなるだけ、という問題でもない。
流石に Predictive Back で前の画面をうっすら表示して、やっぱり元の画面に戻って...を指を離さずに繰り返す処理をしても何度も LaunchedEffect が入場するという挙動にはならない。つまり画面から指を離さずに10回ジェスチャ動作を繰り返しても、前の画面は1回目のジェスチャ処理で入場を完了していて退場はしないので、LaunchedEffect が動くのは一回だけだ。
それは困る、というケースはあるだろう。例えば Karte で View イベントを飛ばす、というのを考えた時、実際には戻っていないが View イベントが飛んでいる。これが許容できない、というケースはあると思う。
これを防ぐ上で、LaunchedEffect で書く方向のままで対応したいんだよ、ということであれば、LifecycleEventEffect に置き換えるだけで済むと思う。
@Composable
fun MyScreen() {
LifecycleEventEffect(Lifecycle.Event.ON_RESUME) {
}
}
onResume とツイになる onPause 時の処理も書くのであれば LifecycleResumeEffect の方が楽。
@Composable
fun MyScreen() {
LifecycleResumeEffect(Unit) {
onPauseOrDispose {
}
}
}
onPauseOrDispose相当は不要ということであれば、LifecycleEventEffect の方がシンプルで良いと思う。
Karte Event を送るだけなら、特化した Composable を書いても良いと思う。
sealed interface KarteEvent {
val name: String
val params: Map<String, Any>
data class MyScreen(
override val params: Map<String, Any>
): KarteEvent {
override val name: String = "my_screen"
}
}
@Composable
fun TrackKarteEventEffect(karteEvent: KarteEvent) {
LifecycleEventEffect(Lifecycle.Event.ON_RESUME) {
}
}
@Composable
fun MyScreen() {
TrackKarteEventEffect(KarteEvent.MyScreen(mapOf("param1" to "value1")))
}
これで、Predictive Back が終わったタイミングで Karte にイベントが飛んでいく、というものは書ける。
これを契機に、闇雲に LaunchedEffect で処理を書いていたのなら、本当に良かったのかを見直しても良さそう。例えば ViewModel の init ブロックでよかったとか、もっと他の場所で処理すれば事足りる何かなのかもしれない。