Reputation: 790
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!
Upvotes: 1
Views: 1009
Reputation: 790
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.
Upvotes: 3