我有一个注入了 hilt 的视图模型,它可以控制加载微调器的状态,这是一个全屏对话框。
@HiltViewModel
class MyViewModel @Inject constructor(
val coordinator: MyCoordinator
) : ViewModel() {
private val _isChanging = MutableStateFlow(false)
val isChanging = _isChanging.asStateFlow()
fun change() {
_isChanging.value = true
// suspend function which takes about 5 seconds to resolve
coordinator.change()
_isChanging.value = false
}
}
我的协调员
val applicationScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
fun change() {
applicationScope.launch {
// do something for 5 seconds
delay(5000)
}
}
最后是用户界面
@Composable
fun MyScreen(
viewModel: MyViewModel = hiltViewModel()
) {
val changing by viewModel.isChanging.collectAsStateWithLifecycle()
if (changing) {
AnimatedLoadingSpinner()
}
// other ui, including a button that triggers viewModel::change in the onClick lambda
}
当我按下按钮时,更改功能确实运行,但加载微调器从未显示。我怎样才能显示加载微调器?
我感觉这与协调器中运行的协程有关。我认为当 change 被调用时,它会将 isChanging 设置为 true,启动协程,然后立即将 isChanging 重新设置为 false。但我不知道如何解决这个问题,除了将 applicationScope 推送到 viewmodel 中,或者将 isChanging.value = x 作为 lambda 传递到 change 函数中,以便可以在 applicationScope 中调用它。
launch
启动一个新的协程并立即返回。新的协程与当前代码异步运行,这就是它的用途。但这也意味着剩余的代码不会等待该协程完成。因此,它会在协程启动后直接调用,而不是在协程完成_isChanging.value = false
后调用。有多种方法可以重构代码以实现您想要的效果。这是我脑海中想到的,但可能还有更多:
coordinator.change
一个挂起函数并从视图模型启动协程(使用viewModelScope
)。然后,您可以将更新放入块_isChanging
内launch
。coordinator.change
提供一个在之后执行的回调delay
。我不推荐这样做,因为它不符合清洁架构原则。在这种情况下,您甚至不再需要视图模型的
_isChanging
流。