lifecycle-runtime-compose 2.8.0-alpha02 から dropUnlessResumed
と dropUnlessStarted
というのが追加されています。これがどういったものかと使用例について解説します。
⚠️ まだalphaなので今後変更されることがあります。
dropUnlessResumed / dropUnlessStarted
まず単純な使い方としては以下のようになります。 Composable関数になっているので、Composable関数からしか使用できません。
val resumed = dropUnlessResumed {
// do something
}
resumed.invoke()
val started = dropUnlessStarted {
// do something
}
started.invoke()
この2つは現在のライフサイクルが少なくても RESUMED
または STARTED
の状態のときのみブロック内が実行されるものになります。
これはすぐに実行されるものではなく、戻り値として () -> Unit
が返されるので、それを呼び出すことで実行されます。
実際には以下のように Button
の onClick
などに対して設定することになると思います。
Button(
onClick = dropUnlessResumed {
// do something
}
) {
Text(text = "Test")
}
実装
実際の中身の実装としては現在のLifecycleから Lifecycle.State#isAtLeast
でチェックして実行するかどうか制御してるだけでした。
引数には任意で LifecycleOwner
を指定することもできます。
使い所
もっとも有効な使い所としては、画面遷移と組み合わせる方法になります。
その前にまずはComposeの画面遷移の問題点に簡単に解説しておきます。
画面遷移の問題点
例えば、以下のように画面遷移を行う2つのボタンがあったとします。
Button(
onClick = {
navController.navigate("next1")
}
) {
Text(text = "Next 1")
}
Button(
onClick = {
navController.navigate("next2")
}
) {
Text(text = "Next 2")
}
このときに2本の指を使い、同時に2つのボタンを押したらどうなるでしょうか?
この場合は、どちらのボタンも実行され画面遷移が2度行われることになります。こおそらく多くの開発者は想定していない挙動だと思います。
既存の対応方法
これに対しては一応は自前でLifecycleを遷移前にチェックすることで対応することができます。
compose-samples で使用されています。
dropUnlessResumed / dropUnlessStarted を使った対応
今回新しく追加されたこの2つを使って同じような対応をすることができます。 navigate
メソッドをこちらを使って呼び出すようにするだけです。
Button(
onClick = dropUnlessResumed { // dropUnlessResumedを使うことで重複して画面遷移しない
navController.navigate("next1")
}
) {
Text(text = "Next 1")
}
まだ正式なドキュメントは公開されてないですが、コード上のコメントで画面遷移時に使用することを推奨しています。
For Navigation users, it’s recommended to safeguard navigate methods when using them while a composable is in transition as a result of navigation.