In my current Android project I have a dialog that retrieves a list of objects from a webservice and show these in a list. It has a problem though. The webservice (outside my control) is not the fastest, so the process takes a while and often the user changes the orientation of the device while this process is ongoing. For now the orientation change results in the original webservice call to be cancelled and a new one is created, but this is not how it should be done I know. I would like the dialog to able to continue the loading from the webservice when an orientation change happens, but I just can't get my head around how to do this. How is it possible to hook into an ongoing call and display this state in the view (the dialog)? Any recommendations are appreciated. I've played around with Android Architecture Components and kotlins sealed classes saving the viewstate in a livedata object that the view observes, but have not found a solution that I like.
I believe a lot of developers use RxJava for this kind of issue. Is this my only option?
Btw. at the moment I use MVP architecture in my project.
Edit
This is where I cancel the job - if the listener is null its cancelled (I'm using kotlin coroutines):
override fun getWorklist() {
job = onWorkerThread {
val result = repository.getResult().awaitResult()
onMainThread {
when (result) {
is Result.Ok -> listener?.onResult(result.getOrDefault(emptyList())) :? job.cancel()
// Any HTTP error
is Result.Error -> listener?.onHttpError(result.exception) :? job.cancel()
// Exception while request invocation
is Result.Exception -> listener?.onException(result.exception) :? job.cancel()
}
}
}
}
Edit 2
I've tried controlling the viewstate using this Android Arch ViewModel, but the MediatorLiveData object isn't triggered on changes of the viewstate object. And the view stays in Loading state:
class MainModel : ViewModel() {
private var job: Job? = null
val viewState = MutableLiveData<MainViewState>().apply { value = Loading("Warsaw") }
val dataGetter = MediatorLiveData<MainViewState>().apply {
addSource(viewState, {
Log.d("MainModel", "Viewstate is: " + viewState.value.toString() + ")...")
when(it) {
is Loading -> {
Log.d("MainModel", "Launching coroutine...")
job = launch(UI) {
Log.d("MainModel", "Loading...")
val items = Repository.getWorklist()
Log.d("MainModel", "Charts retrieved...")
this@MainModel.viewState.postValue(Success(it.items))
Log.d("MainModel", "Posted update to viewstate...")
}
}
}
})
}
override fun onCleared() {
Log.d("MainModel", "Clearing ViewModel")
job?.cancel()
}
}
interface ViewState
sealed class MainViewState : ViewState
class Loading() : MainViewState()
class Error(val error: String) : MainViewState()
class Success(val items: List<WorklistItem>) : MainViewState()