簡體   English   中英

如何安全地(生命周期感知).collectAsState() 一個 StateFlow?

[英]How to safely (lifecycle aware) .collectAsState() a StateFlow?

根據這些文章,我正在嘗試遵循官方指南,使用 Compose 從 LiveData 遷移到 Flow/StateFlow:

從 Android UI 收集流量的更安全方法

從 LiveData 遷移到 Kotlin 的 Flow

我正在嘗試遵循第一篇文章中的建議,在接近結尾的 Jetpack Compose 部分的安全流集合中

在 Compose 中,副作用必須在受控環境中執行。 為此,使用 LaunchedEffect 創建一個遵循可組合項生命周期的協程。 在其塊中,如果您需要它在主機生命周期處於某個 State 時重新啟動代碼塊,則可以在其塊中調用暫停 Lifecycle.repeatOnLifecycle。

我設法以這種方式使用.flowWithLifecycle()來確保當應用程序進入后台時流不會發出:

@Composable
fun MyScreen() {

    val lifecycleOwner = LocalLifecycleOwner.current

    val someState = remember(viewModel.someFlow, lifecycleOwner) {
        viewModel.someFlow
            .flowWithLifecycle(lifecycleOwner.lifecycle, Lifecycle.State.STARTED)
            .stateIn(
                scope = viewModel.viewModelScope,
                started = SharingStarted.WhileSubscribed(5000),
                initialValue = null
            )
    }.collectAsState()

}

我覺得這很“樣板”——一定有更好的東西。 我想在 ViewModel 中使用 StateFlow,而不是在 @Composable 中轉換為 StateFLow 的 Flow,並使用.repeatOnLifeCycle() ,因此我可以使用多個.collectAsState()和更少的樣板。

當我嘗試在協程 (LaunchedEffect) 中使用.collectAsState() 時,我顯然收到了一個錯誤 about.collectAsState() 必須從 @Composable function 的上下文中調用。

我怎樣才能實現與 with.collectAsState() 類似的功能,但在.repeatOnLifecycle() 內部。 我是否必須在 StateFlow 上使用.collect(),然后用 State 包裝該值? 沒有比這更少的樣板嗎?

基於 OP 的回答,如果您不關心WhileSubscribed(5000)行為,則內部不通過StateFlow可能會更輕量級。

@Composable
fun <T> Flow<T>.toStateWhenStarted(initialValue: T): State<T> {
    val lifecycleOwner = LocalLifecycleOwner.current
    return produceState(initialValue = initialValue, this, lifecycleOwner) {
        lifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
            collect { value = it }
        }
    }
}

在閱讀了幾篇文章后,包括

Flow 的 shareIn 和 stateIn 操作符須知

repeatOnLifecycle API 設計故事

我越來越相信以這種方式使用.flowWithLifecycle() 而不是repeatOnLifecycle() +produceState()。 我剛剛做了這個擴展 function 以在從@Composable 收集流為 State 時減輕樣板:

@Composable
fun <T> Flow<T>.flowWithLifecycleStateInAndCollectAsState(
    scope: CoroutineScope,
    initial: T? = null,
    context: CoroutineContext = EmptyCoroutineContext,
): State<T?> {
    val lifecycleOwner = LocalLifecycleOwner.current
    return remember(this, lifecycleOwner) {
        this
            .flowWithLifecycle(lifecycleOwner.lifecycle, Lifecycle.State.STARTED)
            .stateIn(
                scope = scope,
                started = SharingStarted.WhileSubscribed(5000),
                initialValue = initial
            )
    }.collectAsState(context)
}

這將在 @Composable 中像這樣使用:

@Composable
fun SomeScreen() {

//...

    val someState = viewModel.someFlow
        .flowWithLifecycleStateInAndCollectAsState(
            scope = viewModel.viewModelScope  //or the composable's scope
        )

//...

}

我會將其標記為已接受的解決方案,以防它對其他人有所幫助,除非有人在接下來的幾天內讓我知道更好的方法。

“androidx.lifecycle:lifecycle-runtime-compose:2.6.0-alpha01”中,您可以使用collectAsStateWithLifecycle()擴展 function 從流/狀態流中收集並以生命周期感知方式將其最新值表示為 Compose State。

import androidx.lifecycle.compose.collectAsStateWithLifecycle

@Composable
fun MyScreen() {
    val state by viewModel.state.collectAsStateWithLifecycle()
}

來源: Android 生命周期發布

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM