during the last two days I read a lot about the rxJava retryWhen operator. Here, here, here and some more I forgot.
But unfortunately I'm not able to get it work. What I'm trying to achive is, that I make an API call. If the call returns an error, I'm showing a SnackBar to the user with a reload button. If the user clicks this button, I would like to resubscribe to the chain.
Here is my code:
interface RetrofitApi {
@GET("/v1/loadMyData")
fun getMyData(): Single<Response<DataResponse>>
}
Where Response is from retrofit2. I need it to wrap the data class to check if response is successful.
The next fun is called in a repository from a ViewModel:
override fun loadMyData(): Observable<Resource<DataResponse>> {
return retrofitApi
.getMyData()
.compose(getRetryTransformer())
.toObservable()
.compose(getResponseTransformer())
}
Resource is another wrapper for the state of the call (SUCCESS, ERROR, LOADING).
And finally the Transformer:
private fun <Data> getRetryTransformer(): SingleTransformer<Response<Data>, Response<Data>> {
return SingleTransformer { singleResponse ->
singleResponse
.onErrorReturn {
singleResponse.blockingGet()
}
.retryWhen { errors ->
errors.zipWith(retrySubject.toFlowable(BackpressureStrategy.LATEST),
BiFunction<Throwable, Boolean, Flowable<Throwable>> { throwable: Throwable, isRetryEnabled: Boolean ->
if (isRetryEnabled) {
Flowable.just(null)
} else {
Flowable.error(throwable)
}
})
}
}
}
The retrySubject:
private val retrySubject = PublishSubject.create<Boolean>()
And when the user clicks the retry button, I call:
retrySubject.onNext(true)
The problem is now, that the error is not returned to the ViewModel and the SnackBar is never shown. I tried onErrorResumeNext() as well with no success. The whole retryWhen/zipWith part seem to work. Because in the repository there some more API calls with no retry behavior (yet) and there the SnackBar is displayed. That means, I do anther call where the SnackBar is shown -> button click and the retry transform works as expected.
If you need some more information please don't hesitate to ask! Any help is appreciated!
Strange, as soon you do it the right way, it works. I over read somehow that I need in doOnError{...} to manage to show my Snackbar.
Here is my working retry transformer:
private fun <Data> getRetryTransformer(): SingleTransformer<Response<Data>, Response<Data>> {
return SingleTransformer { singleResponse ->
singleResponse
.doOnError {
errorEventSubject.onNext(it)
}
.retryWhen { errors ->
errors.zipWith(retrySubject.toFlowable(BackpressureStrategy.LATEST),
BiFunction<Throwable, Boolean, Flowable<Throwable>> { throwable: Throwable, isRetryEnabled: Boolean ->
if (isRetryEnabled) {
Flowable.just(throwable)
} else {
Flowable.error(throwable)
}
})
}
}
}
And the chain looks now like this (and I think it's beautiful):
override fun loadMyData(): Observable<Resource<DataResponse>> {
return retrofitApi
.getMyData()
.compose(getRetryTransformer())
.toObservable()
.compose(getResponseTransformer())
}
What else I needed to propagate the error to my ViewModel is a 2nd PublishSubject:
private val errorEventSubject = PublishSubject.create<Throwable>()
And in the ViewModel I observe the changes for it and show the Snackbar. That's it.