我看到这篇文章
https://proandroiddev.com/loading-initial-data-in-launchedeffect-vs-viewmodel-f1747c20ce62
其中作者描述了数据初始化方法的优点和缺点,得出的结论是最好通过StateFlow
.我决定尝试一下,前提是我使用UiState
包装器作为屏幕的一般状态:
sealed class UiState<out T> {
object Loading : UiState<Nothing>()
data class Error(val code: Int? = null, val message: String? = null) : UiState<Nothing>()
data class Content<T>(val data: T) : UiState<T>()
}
也就是说,我在更改之前的实现:
class MyRepo {
fun getMyData(): Flow<List<String>> {
return flow {
delay(1000)
emit(listOf("1", "2"))
}
}
}
class MyViewModel1(repo: MyRepo): ViewModel() {
data class ScreenStateUI(
val data: List<String> = emptyList(),
val title: String = "Title"
)
private val _screenUiState: MutableStateFlow<UiState<ScreenStateUI>> = MutableStateFlow(UiState.Loading)
val screenUiState: StateFlow<UiState<ScreenStateUI>> = _screenUiState.asStateFlow()
init {
viewModelScope.launch {
repo.getMyData()
.map<List<String>, UiState<ScreenStateUI>> { UiState.Content(ScreenStateUI(data = it)) }
.collectLatest {
if (it is UiState.Content) {
_screenUiState.emit(
UiState.Content(data = it)
)
}
}
}
fun updateTitle(title: String) {
_screenUiState.update {
if (it is UiState.Content) {
it.copy(data = it.data.copy(title = title))
} else {
it
}
}
}
}
初始化期间,状态被加载,UI可以通过订阅变化screenUiState
,也就是说,如果需要使用它fun updateTitle(title: String)
,没有问题。
现在,我更改了实现以摆脱init
块中的初始化,并在 UI 订阅事件后立即执行所有操作:
class MyRepo {
fun getMyData(): Flow<List<String>> {
return flow {
delay(1000)
emit(listOf("1", "2"))
}
}
}
class MyViewModel2(repo: MyRepo): ViewModel() {
data class ScreenStateUI(
val data: List<String> = emptyList(),
val title: String = "Title"
)
val screenUiState: StateFlow<UiState<ScreenStateUI>> by lazy {
repo.getMyData()
.map<List<String>, UiState<ScreenStateUI>> { UiState.Content(ScreenStateUI(data = it)) }
.onStart { emit(UiState.Loading) }
.catch { emit(UiState.Error(message = it.message)) }
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = UiState.Loading
)
}
fun updateTitle(title: String) {
...
}
}
也就是说,一切似乎都不错,实际上现在 UI 仅在订阅时才会发起请求,但现在状态无法更改,因此该方法fun updateTitle(title: String)
无法更新title
。
我在这里缺少什么?