gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-04 23:44:15 25 4
gpt4 key购买 nike

根据这些文章,我正在尝试遵循官方指南,使用 Compose 从 LiveData 迁移到 Flow/StateFlow:
A safer way to collect flows from Android UIs
Migrating from LiveData to Kotlin’s Flow
我正在尝试遵循 第一篇文章中的建议。 Jetpack Compose 中的安全流量收集 部分接近尾声。

In Compose, side effects must be performed in a controlledenvironment. For that, use LaunchedEffect to create a coroutine thatfollows the composable’s lifecycle. In its block, you could call thesuspend Lifecycle.repeatOnLifecycle if you need it to re-launch ablock of code when the host lifecycle is in a certain State.


我已经设法使用 .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() 时,我显然会收到关于必须从 @Composable 函数的上下文中调用 .collectAsState() 的错误。
如何实现与 .collectAsState() 类似的功能,但在 .repeatOnLifecycle() 内部。我是否必须在 StateFlow 上使用 .collect() 然后用 State 包装值?没有比这更少的样板吗?

最佳答案

在阅读了几篇文章后,包括
Things to know about Flow’s shareIn and stateIn operators
repeatOnLifecycle API design story
并最终意识到我想在 ViewModel 中而不是在可组合中拥有 StateFlow,我想出了这两个解决方案:
1. 我最终使用的是,这对于驻留在 ViewModel 中的多个 StateFlows 更好,这些 StateFlows 需要在后台收集,同时有来自 UI 的订阅者(在这种情况下,加上 5000 毫秒的延迟来处理配置更改,例如屏幕旋转, UI 仍然对数据感兴趣,因此我们不想重新启动 StateFlow 收集例程)。在我的例子中,原始 Flow 来自 Room,并在 VM 中成为 StateFlow,因此应用程序的其他部分可以访问最新数据。

class MyViewModel: ViewModel() {

//...

val someStateFlow = someFlow.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = Result.Loading()
)
val anotherStateFlow = anotherFlow.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = Result.Loading()
)

//...
}
然后在 UI 中收集:
@Composable
fun SomeScreen() {

var someUIState: Any? by remember { mutableStateOf(null)}
var anotherUIState: Any? by remember { mutableStateOf(null)}

LaunchedEffect(true) {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
viewModel.someStateFlow.collectLatest {
someUIState = it
}
}
launch {
viewModel.anotherStateFlow.collectLatest {
anotherUIState = it
}
}
}
}
}

2. 收集 时减轻样板的扩展功能单 StateFlow 作为 @Composable 中的状态。这仅在我们有个人 时才有用。热门 不会与 UI 的其他屏幕/部分共享的流,但在任何给定时间仍需要最新数据(像这样使用 .stateIn 运算符创建的热流将继续在后台收集,在行为上有一些差异取决于启动的参数)。如果冷流足以满足我们的需求,我们可以将 .stateIn 运算符连同初始参数和范围参数一起删除,但在这种情况下,没有那么多样板文件,我们可能不需要这个扩展函数。

@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
)

//...

}

关于android - 如何安全地(生命周期感知).collectAsState() 一个 StateFlow?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70645699/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com