LoginSignup
26
8

More than 1 year has passed since last update.

collectAsStateWithLifecycleが追加されたぞ

Last updated at Posted at 2022-06-30

はじめに

こんにちは、どすこいです。
Androidエンジニアのみなさん、Compose楽しんでますか?
僕は楽しんでます。

今回jetpackのlifecycle-runtime-compose:2.6.0-alphacollectAsStateWithLifecycle()なるものが追加されたので早速触っていきたいと思います。

前提

まずComposeで扱う値をUiStateにまとめたものとします。
そのUiStateをViewModel側でStateFlowとして持っているという状態です。

こんな感じ

MainViewModel.kt
class MainViewModel : ViewModel() {
    val _state: MutableStateFlow<UiState> = MutableStateFlow(UiState())
    val state: StateFlow<UiState> = _state.asStateFlow()

    data class UiState(
        val hoge: String = ""
    )
}

このStateFlowをComposable側で監視する際にどうやっていくのかを見ていきます

試してみる

Lifecycleを考慮しない場合のStateFlowの監視方法

これだとLifecycleが何も考慮されてないのでアプリがバックグラウンドにある状態でStateFlowが更新されるとrecompositionされてしまうと思います。

@Composable
fun MainScreen(mainViewModel: MainViewModel) {
	val state by mainViewModel.state.collectAsState(initial = MainViewModel.UiState())
}

Lifecycleを考慮した場合のStateFlowの監視方法

こちらだとflowWithLifecycleを使っており、中でrepeatOnLifecycleを使っているので、指定したLifecycle.State外でStateFlowが更新されてもrecompositionされません。
つまりLifecycle.State.STARTEDからLifecycle.State.STOPEDより外でStateFlowが変更されてもrecompositionされないというわけですね。
ただ毎回これを書くのはめんどくさいので、多くのエンジニアは独自で共通の関数を作ってたと思います。

@Composable
fun MainScreen(mainViewModel: MainViewModel) {
	val lifecycleOwner = LocalLifecycleOwner.current
	val flowLifecycleAware = remember(key1 = mainViewModel, key2 = lifecycleOwner) {
		mainViewModel.state.flowWithLifecycle(lifecycleOwner.lifecycle, Lifecycle.State.STARTED)
	}
	val state by flowLifecycleAware.collectAsState(initial = MainViewModel.UiState())
}

collectAsStateWithLifecycle()を使った場合の監視方法

こちらのIssueTrackerにもあるように、デフォで上記のことをやってくれる関数を用意してくれとの要望があったそうです。
Googleさんはとても優しいのでLifecycleを考慮したcollectAsStateを用意してくれました

@ExperimentalLifecycleComposeApi
@Composable
fun <T> Flow<T>.collectAsStateWithLifecycle(
    initialValue: T,
    lifecycle: Lifecycle,
    minActiveState: Lifecycle.State = Lifecycle.State.STARTED,
    context: CoroutineContext = EmptyCoroutineContext
): State<T> {
    return produceState(initialValue, this, lifecycle, minActiveState, context) {
        lifecycle.repeatOnLifecycle(minActiveState) {
            if (context == EmptyCoroutineContext) {
                this@collectAsStateWithLifecycle.collect { this@produceState.value = it }
            } else withContext(context) {
                this@collectAsStateWithLifecycle.collect { this@produceState.value = it }
            }
        }
    }
}

デフォルトのminActiveStateLifecycle.State.STARTEDなので特にこちら側で何か指定しない限りはLifecycle.State.STARTEDからLifecycle.State.STOPEDまではrecompositionしてくれますが、それより外のLifecycleでStateFlowが更新されてもrecompositionされないということですね。
下が実装例です。

@Composable
fun MainScreen(mainViewModel: MainViewModel) {
	// デフォがLifecycle.State.STARTEDなので基本的には何も指定しなくて良い
	val state by mainViewModel.state.collectAsStateWithLifecycle()
}

注意

artifactがlifecycle-runtimeではなくて、新しく追加されたlifecycle-runtime-composeなので注意
あとはまだalpha版なので実戦投入するときは注意してください。

26
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
8