- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
根据这些文章,我正在尝试遵循官方指南,使用 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.
@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()样板更少。
最佳答案
在阅读了几篇文章后,包括
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/
我是一名优秀的程序员,十分优秀!