// Modified version of https://github.com/square/retrofit/blob/master/retrofit/src/main/java/retrofit/ExecutorCallAdapterFactory.java public class ErrorHandlingExecutorCallAdapterFactory implements CallAdapter.Factory { private final Executor callbackExecutor; ErrorHandlingExecutorCallAdapterFactory(Executor callbackExecutor) { this.callbackExecutor = callbackExecutor; } @Override public CallAdapter> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { if ($Gson$Types.getRawType(returnType) != Call.class) { return null; } final Type responseType = getCallResponseType(returnType); return new CallAdapter>() { @Override public Type responseType() { return responseType; } @Override public Call adapt(Call call) { return new ExecutorCallbackCall<>(callbackExecutor, call); } }; } static final class ExecutorCallbackCall implements Call { private final Executor callbackExecutor; private final Call delegate; ExecutorCallbackCall(Executor callbackExecutor, Call delegate) { this.callbackExecutor = callbackExecutor; this.delegate = delegate; } @Override public void enqueue(Callback callback) { delegate.enqueue(new ExecutorCallback<>(callbackExecutor, callback)); } @Override public Response execute() throws IOException { return delegate.execute(); } @Override public void cancel() { delegate.cancel(); } @SuppressWarnings("CloneDoesntCallSuperClone") // Performing deep clone. @Override public Call clone() { return new ExecutorCallbackCall<>(callbackExecutor, delegate.clone()); } } static final class ExecutorCallback implements Callback { private final Executor callbackExecutor; private final Callback delegate; ExecutorCallback(Executor callbackExecutor, Callback delegate) { this.callbackExecutor = callbackExecutor; this.delegate = delegate; } @Override public void onResponse(final Response response, final Retrofit retrofit) { if (response.isSuccess()) { callbackExecutor.execute(new Runnable() { @Override public void run() { delegate.onResponse(response, retrofit); } }); } else { callbackExecutor.execute(new Runnable() { @Override public void run() { delegate.onFailure(RetrofitException.httpError(response.raw().request().urlString(), response, retrofit)); } }); } } @SuppressWarnings("ThrowableResultOfMethodCallIgnored") @Override public void onFailure(final Throwable t) { RetrofitException exception; if (t instanceof IOException) { exception = RetrofitException.networkError((IOException) t); } else { exception = RetrofitException.unexpectedError(t); } final RetrofitException finalException = exception; callbackExecutor.execute(new Runnable() { @Override public void run() { delegate.onFailure(finalException); } }); } } public static class MainThreadExecutor implements Executor { private final Handler handler = new Handler(Looper.getMainLooper()); @Override public void execute(@NonNull Runnable r) { handler.post(r); } } static Type getCallResponseType(Type returnType) { if (!(returnType instanceof ParameterizedType)) { throw new IllegalArgumentException( "Call return type must be parameterized as Call or Call"); } final Type responseType = getSingleParameterUpperBound((ParameterizedType) returnType); // Ensure the Call response type is not Response, we automatically deliver the Response object. if ($Gson$Types.getRawType(responseType) == retrofit.Response.class) { throw new IllegalArgumentException( "Call cannot use Response as its generic parameter. " + "Specify the response body type only (e.g., Call)."); } return responseType; } public static Type getSingleParameterUpperBound(ParameterizedType type) { Type[] types = type.getActualTypeArguments(); if (types.length != 1) { throw new IllegalArgumentException( "Expected one type argument but got: " + Arrays.toString(types)); } Type paramType = types[0]; if (paramType instanceof WildcardType) { return ((WildcardType) paramType).getUpperBounds()[0]; } return paramType; } }