Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
CustomRxJava2CallAdapterFactory to transform HttpExceptions to custom error
import io.reactivex.Completable;
import io.reactivex.Flowable;
import io.reactivex.Maybe;
import io.reactivex.MaybeSource;
import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.Scheduler;
import io.reactivex.Single;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import retrofit2.Call;
import retrofit2.CallAdapter;
import retrofit2.HttpException;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import timber.log.Timber;
public class CustomRxJava2CallAdapterFactory extends CallAdapter.Factory {
private final RxJava2CallAdapterFactory originalFactory;
private final Gson gson = new Gson();
public static CustomRxJava2CallAdapterFactory create() {
return new CustomRxJava2CallAdapterFactory(null);
public static CallAdapter.Factory createWithScheduler(Scheduler scheduler) {
return new CustomRxJava2CallAdapterFactory(scheduler);
private CustomRxJava2CallAdapterFactory(Scheduler scheduler) {
if (scheduler == null) {
originalFactory = RxJava2CallAdapterFactory.create();
} else {
originalFactory = RxJava2CallAdapterFactory.createWithScheduler(scheduler);
@Override public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
return new RxCallAdapterWrapper(originalFactory.get(returnType, annotations, retrofit), gson);
private static class RxCallAdapterWrapper<R> implements CallAdapter<R, Object> {
private final CallAdapter<R, Object> wrapped;
private Gson gson;
RxCallAdapterWrapper(CallAdapter<R, Object> wrapped, Gson gson) {
this.wrapped = wrapped;
this.gson = gson;
@Override public Type responseType() {
return wrapped.responseType();
@Override public Object adapt(Call<R> call) {
Object object = wrapped.adapt(call);
if (object instanceof Observable<?>) {
return ((Observable<?>) object).onErrorResumeNext(
(ObservableSource) throwable -> Observable.error(transformException((Throwable) throwable, call)));
} else if (object instanceof Single<?>) {
return ((Single<?>) object).onErrorResumeNext(throwable -> Single.error(transformException(throwable, call)));
} else if (object instanceof Completable) {
return ((Completable) object).onErrorResumeNext(
throwable -> Completable.error(transformException((Throwable) throwable, call)));
} else if (object instanceof Maybe<?>) {
return ((Maybe<?>) object).onErrorResumeNext(
(MaybeSource) throwable -> Maybe.error(transformException((Throwable) throwable, call)));
} else if (object instanceof Flowable<?>) {
return ((Flowable<?>) object).onErrorResumeNext(throwable -> {
return Flowable.error(transformException(throwable, call));
return object;
private Throwable transformException(Throwable throwable, Call call) {
// We can also access the initial call.request().url() e.g. to detect which route caused an exception
if (throwable instanceof HttpException) {
MyApiError apiError = null;
Response<?> response = ((HttpException) throwable).response();
if (response.errorBody() != null) {
try {
apiError = gson.fromJson(response.errorBody().string(), MyCustomError.class);
} catch (Exception e) {
if (apiError != null) {
return apiError;
return throwable;

This comment has been minimized.

Copy link

aliumujib commented Oct 16, 2019


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.