In my personal project, I'm using Hilt (dependency injection), SavedStateHandle and ViewModel.
SavedStateHandle which allows me to save data across process death.
Because ViewModel doesn't survive across process death, I'm injecting inside the ViewModel the savedStateHandle object using Hilt injection.
when using coroutines/viewModelScope to handle http requests, I'm using a custom class "ViewState" to handle the multiple state in just one LiveData which is observed from UI.
For example : LiveData<ViewState<User>>
and then in UI, I'm binding the 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
}
}
}
In ViewState.Loading class, job is transient because it is not serializable. And before using SavedStateHandle in viewModel, I didn't need to serialize the LiveData value because it was initialized to null when ViewModel was created.
The problem is
When the app process is killed while the viewState of user is ViewState.Loading, viewModelScope is no longer alive and the job associated to it is cancelled in onCleared() from ViewModel.
Naively, I thought that when the "onCleared" event occurs, I had to just change the value of LiveData to null or whatever I want to avoid a ViewState.Loading value across process death but apparently the SavedStateHandle is already saved at this time; because otherwise when the app state is restored, the user view state is ViewState.Loading() and I'm waiting for something which will never happen because the job is already cancelled. When serializing the SavedStateHandle, I should never save a ViewState.Loading object. So in practice "job" variable should not be transient because it will never be serialized.
How can I intercept the exact time when SavedStateHandle is serialized/parcelized to avoid this kind of problem ?
I guess you cannot (unless you do the saving/restoring yourself, inside the Activity/Fragment).
However, instead of preventing to store the Loading state, you could by-pass it once your restore it. E.g. when your restored state is Loading, you could call the loading function again.