在我的个人项目中,我使用 Hilt(依赖注入)、SavedStateHandle 和 ViewModel。
SavedStateHandle 允许我在进程死亡时保存数据。
因为 ViewModel 在进程死亡后无法生存,所以我使用 Hilt 注入在 ViewModel 中注入了 savedStateHandle 对象。
当使用 coroutines/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.Loading 时被杀死时,viewModelScope 不再处于活动状态,并且与其关联的作业在 ViewModel 的 onCleared() 中被取消。
天真地,我认为当“onCleared”事件发生时,我必须将 LiveData 的值更改为 null 或任何我想避免 ViewState.Loading 值跨进程死亡但显然 SavedStateHandle 此时已经保存;因为否则当应用程序状态恢复时,用户视图状态是 ViewState.Loading() 并且我正在等待由于作业已经取消而永远不会发生的事情。序列化 SavedStateHandle 时,我永远不应该保存 ViewState.Loading 对象。所以在实践中“工作”变量不应该是瞬态的,因为它永远不会被序列化。
如何截取 SavedStateHandle 被序列化/打包的确切时间以避免此类问题?