RxJava + Retrofit -> BaseObservable for API calls for centralized response handling
Two links you provided are a really good starting point, which I used to construct solution to react to accidental
- network errors happen sometimes due to temporary lack of network connection, or switch to low throughtput network standard, like EDGE, which causes SocketTimeoutException
- server errors -> happen sometimes due to server overload
I have overriden CallAdapter.Factory
to handle errors and react appropriately to them.
Import
RetryWithDelayIf
from the solution you foundOverride
CallAdapter.Factory
to handle errors:public class RxCallAdapterFactoryWithErrorHandling extends CallAdapter.Factory { private final RxJavaCallAdapterFactory original; public RxCallAdapterFactoryWithErrorHandling() { original = RxJavaCallAdapterFactory.create(); } @Override public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { return new RxCallAdapterWrapper(retrofit, original.get(returnType, annotations, retrofit)); } public class RxCallAdapterWrapper implements CallAdapter<Observable<?>> { private final Retrofit retrofit; private final CallAdapter<?> wrapped; public RxCallAdapterWrapper(Retrofit retrofit, CallAdapter<?> wrapped) { this.retrofit = retrofit; this.wrapped = wrapped; } @Override public Type responseType() { return wrapped.responseType(); } @SuppressWarnings("unchecked") @Override public <R> Observable<?> adapt(final Call<R> call) { return ((Observable) wrapped.adapt(call)).onErrorResumeNext(new Func1<Throwable, Observable>() { @Override public Observable call(Throwable throwable) { Throwable returnThrowable = throwable; if (throwable instanceof HttpException) { HttpException httpException = (HttpException) throwable; returnThrowable = httpException; int responseCode = httpException.response().code(); if (NetworkUtils.isClientError(responseCode)) { returnThrowable = new HttpClientException(throwable); } if (NetworkUtils.isServerError(responseCode)) { returnThrowable = new HttpServerException(throwable); } } if (throwable instanceof UnknownHostException) { returnThrowable = throwable; } return Observable.error(returnThrowable); } }).retryWhen(new RetryWithDelayIf(3, DateUtils.SECOND_IN_MILLIS, new Func1<Throwable, Boolean>() { @Override public Boolean call(Throwable throwable) { return throwable instanceof HttpServerException || throwable instanceof SocketTimeoutException || throwable instanceof UnknownHostException; } })); } } }
HttpServerException
is just a custom exception.Use it in
Retrofit.Builder
Retrofit retrofit = new Retrofit.Builder() .addCallAdapterFactory(new RxCallAdapterFactoryWithErrorHandling()) .build();
Extra: If you wish to parse errors that come from API (error that don't invoke UnknownHostException
, HttpException
or MalformedJsonException
or etc.) you need to override Factory
and use custom one during building Retrofit
instance. Parse the response and check if it contains errors. If yes, then throw error and error will be handled inside the method above.