简体   繁体   English

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

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

I'm trying to follow the official guidelines to migrate from LiveData to Flow/StateFlow with Compose, as per these articles:根据这些文章,我正在尝试遵循官方指南,使用 Compose 从 LiveData 迁移到 Flow/StateFlow:

A safer way to collect flows from Android UIs 从 Android UI 收集流量的更安全方法

Migrating from LiveData to Kotlin's Flow 从 LiveData 迁移到 Kotlin 的 Flow

I am trying to follow what is recommended in the first article, in the Safe Flow collection in Jetpack Compose section near the end.我正在尝试遵循第一篇文章中的建议,在接近结尾的 Jetpack Compose 部分的安全流集合中

In Compose, side effects must be performed in a controlled environment.在 Compose 中,副作用必须在受控环境中执行。 For that, use LaunchedEffect to create a coroutine that follows the composable's lifecycle.为此,使用 LaunchedEffect 创建一个遵循可组合项生命周期的协程。 In its block, you could call the suspend Lifecycle.repeatOnLifecycle if you need it to re-launch a block of code when the host lifecycle is in a certain State.在其块中,如果您需要它在主机生命周期处于某个 State 时重新启动代码块,则可以在其块中调用暂停 Lifecycle.repeatOnLifecycle。

I have managed to use .flowWithLifecycle() in this way to make sure the flow is not emmiting when the app goes to the background:我设法以这种方式使用.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()

}

I find this very "boilerplatey" -there must be something better.我觉得这很“样板”——一定有更好的东西。 I would like to have StateFlow in the ViewModel, instead of Flow that gets converted to StateFLow in the @Composable, and use .repeatOnLifeCycle() , so I can use multiple .collectAsState() with less boilerplate.我想在 ViewModel 中使用 StateFlow,而不是在 @Composable 中转换为 StateFLow 的 Flow,并使用.repeatOnLifeCycle() ,因此我可以使用多个.collectAsState()和更少的样板。

When I try to use.collectAsState() inside a coroutine (LaunchedEffect), I obviously get an error about.collectAsState() having to be called from the context of @Composable function.当我尝试在协程 (LaunchedEffect) 中使用.collectAsState() 时,我显然收到了一个错误 about.collectAsState() 必须从 @Composable function 的上下文中调用。

How can I achieve similar functionality as with.collectAsState(), but inside.repeatOnLifecycle().我怎样才能实现与 with.collectAsState() 类似的功能,但在.repeatOnLifecycle() 内部。 Do I have to use.collect() on the StateFlow and then wrap the value with State?我是否必须在 StateFlow 上使用.collect(),然后用 State 包装该值? Isn't there anything with less boilerplate than that?没有比这更少的样板吗?

Building upon OP's answer, it can be a bit more light-weight by not going through StateFlow internally, if you don't care about the WhileSubscribed(5000) behavior.基于 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 }
        }
    }
}

After reading a few more articles, including在阅读了几篇文章后,包括

Things to know about Flow's shareIn and stateIn operators Flow 的 shareIn 和 stateIn 操作符须知

repeatOnLifecycle API design story repeatOnLifecycle API 设计故事

I am becoming more convinced of using.flowWithLifecycle() in this way instead of repeatOnLifecycle() + produceState().我越来越相信以这种方式使用.flowWithLifecycle() 而不是repeatOnLifecycle() +produceState()。 I just made this extension function to alleviate the boilerplate when collecting a flow as State from a @Composable:我刚刚做了这个扩展 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)
}

This would then be used like this in a @Composable:这将在 @Composable 中像这样使用:

@Composable
fun SomeScreen() {

//...

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

//...

}

I will mark this as accepted solution in case it helps someone else, unless someone lets me know of a better way in the next few days.我会将其标记为已接受的解决方案,以防它对其他人有所帮助,除非有人在接下来的几天内让我知道更好的方法。

From "androidx.lifecycle:lifecycle-runtime-compose:2.6.0-alpha01" you can use the collectAsStateWithLifecycle() extension function to collect from flow/stateflow and represents its latest value as Compose State in a lifecycle-aware manner.“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()
}

Source: Android Lifecycle release来源: Android 生命周期发布

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在 compose 中收集 StateFlow 作为生命周期感知首先发出初始值,然后发出原始值 - Collecting StateFlow in compose as lifecycle aware first emit initial value then the original value 如何使用 Hilt 创建生命周期感知组件? - How to create lifecycle aware components with Hilt? 是observeForever 生命周期意识吗? - is observeForever lifecycle aware? LifeCycle Aware Codelab概念 - LifeCycle Aware Codelab Concept Flow LifeCycle 是否与 LiveData 一样感知? - Is Flow LifeCycle aware as LiveData? 如何使用Proguard调用可识别Android生命周期的组件? - How to make Android lifecycle aware component get called on using Proguard? 使用 LifecycleObserver 的生命周期感知组件如何感知屏幕方向变化 - How can lifecycle aware components using LifecycleObserver be aware of screen orientation changes 需要权限的生命周期感知组件 - Lifecycle Aware Component that needs Permission 上下文在生命周期感知 LiveEvent 中为空 - Context is null in Lifecycle aware LiveEvent 如何在 Android 的生命周期感知协程范围内返回函数值? - How do I return function value in lifecycle-aware coroutine scope in Android?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM