在我的个人项目中,我使用Hilt(依赖注入(、SavedStateHandle和ViewModel。
SavedStateHandle,它允许我跨进程死亡保存数据。
因为ViewModel无法在进程死亡时存活,所以我使用Hilt注入在ViewModel内部注入savedStateHandle对象。
当使用协程/viewModelScope来处理http请求时,我使用的是一个自定义类";ViewState";以仅在从UI观察到的一个LiveData中处理多个状态。
例如:LiveData<ViewState<User>>
然后在UI中,我绑定viewState:viewMvc.bind(userViewState)
sealed class ViewState<T> : Serializable {
data class Loading<T>(@Transient val job: Job? = null, val oldResult: T? = null) : ViewState<T>(), Serializable
data class Fetched<T>(val result: T, val fetchedDate: Date = Date()) : ViewState<T>(), Serializable
data class Error<T>(val e: Exception, val oldResult: T? = null) : ViewState<T>(), Serializable
override fun toString(): String {
return when (this) {
is Loading -> "ViewState.Loading - oldResult: $oldResult"
is Fetched -> "ViewState.Fetched - result: $result, fetched date: $fetchedDate"
is Error -> "ViewState.Error - class: ${e::class.java.simpleName}, message: ${e.localizedMessage?.toString()}"
}
}
fun lastResult(): T? {
return when (this) {
is Fetched -> result
is Loading -> oldResult
is Error -> oldResult
}
}
}
在ViewState.Loading类中,作业是暂时的,因为它不可序列化。在viewModel中使用SavedStateHandle之前,我不需要序列化LiveData值,因为在创建viewModel时,它被初始化为null。
问题是
当用户的viewState为viewState时应用程序进程被终止。正在加载时,viewModelScope不再活动,并且在ViewModel的onCleared((中取消与其关联的作业。
天真地,我认为当;onCleared";事件发生时,我不得不将LiveData的值更改为null或任何我想避免ViewState的值。在进程死亡期间加载值,但显然此时SavedStateHandle已经保存;因为否则,当应用程序状态恢复时,用户视图状态为ViewState.Loading((,我正在等待一些永远不会发生的事情,因为作业已经取消。在序列化SavedStateHandle时,我永远不应该保存ViewState.Loading对象。因此在实践中";工作;变量不应该是瞬态的,因为它永远不会被序列化。
如何截取SavedStateHandle序列化/分组的确切时间以避免此类问题?
我想你不能(除非你自己在Activity/Fragment中保存/恢复(。
然而,您可以在恢复后绕过它,而不是阻止存储Loading状态。例如,当您恢复的状态为Loading时,您可以再次调用Loading函数。
// Inside your viewModel
init{
if(savedState.contains("Loading)){
viewModelScope.launch{
loadData()
}
}
}