我正在使用 Compose 和 Room 数据库编写一个 Android 应用程序。我有一个可行的解决方案,但我不确定我是否使用了最佳实践。我在 ViewModel 的 init 中收集两个流,以便我可以创建在 ViewModel 的可组合项中使用的 UI 状态:
class MyViewModel(
savedStateHandle: SavedStateHandle,
context: Context
) : ViewModel() {
// Some code omitted
var uiState by mutableStateOf(MyUiState())
init {
viewModelScope.launch {
combine(
myRepo.getMovie(movieId).filterNotNull(),
myRepo.getActors(movieId)
) { movie, actors ->
uiState.copy(
movie = movie,
actorList = actors
)
}.collect { newState ->
uiState = newState
}
}
}
}
根据我的研究,在 init 中调用collect()可能是有问题的,但我很难找到任何硬文档说不要这样做。而且我不知道任何其他方法可以在添加或删除演员时自动更新 uiState 而无需调用collect()。任何有关更新 uiState 的更好解决方案的建议将不胜感激。
总体思路是仅转换视图模型中的流,收集只能在 UI 中完成。这样,UI 可以根据需要订阅和取消订阅流,从而节省资源。
在您的示例中,
uiState
应该是一个流,具体来说,它应该是一个StateFlow
:
val uiState: StateFlow<MyUiState> = transformedFlow(movieId)
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = MyUiState(),
)
Kotlin
StateFlow
在概念上与 Compose State
类似,因为它表示可以观察到变化的单个值。在您的可组合项中,您可以像这样检索 uiState:
val uiState: MyUiState by viewModel.uiState.collectAsStateWithLifecycle()
为此,您需要将依赖项
androidx.lifecycle:lifecycle-runtime-compose
添加到您的 gradle 文件中。请参阅 https://medium.com/androiddevelopers/consuming-flows-safely-in-jetpack-compose-cde014d0d5a3 了解更多信息。
transformedFlow
将是您的存储库的组合流:
private fun transformedFlow(movieId: Int) = combine(
myRepo.getMovie(movieId).filterNotNull(),
myRepo.getActors(movieId)
) { movie, actors ->
MyUiState(
movie = movie,
actorList = actors
)
}