Skip to content

Instantly share code, notes, and snippets.

@koesie10
Created October 3, 2015 07:40
Show Gist options
  • Star 73 You must be signed in to star a gist
  • Fork 17 You must be signed in to fork a gist
  • Save koesie10/bc6c62520401cc7c858f to your computer and use it in GitHub Desktop.
Save koesie10/bc6c62520401cc7c858f to your computer and use it in GitHub Desktop.
Retrofit 1 error handling behaviour in Retrofit 2
// Dagger 1 example
@Module(
complete = false,
library = true
)
public final class ApiModule {
@Provides
@Singleton
Retrofit provideRetrofit(Gson gson, Application app) {
return new Retrofit.Builder()
.baseUrl(app.getString(R.string.base_url))
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(new ErrorHandlingExecutorCallAdapterFactory(new ErrorHandlingExecutorCallAdapterFactory.MainThreadExecutor()))
.build();
}
@Provides
@Singleton
LoginService provideLoginService(Retrofit retrofit) {
return retrofit.create(LoginService.class);
}
}
// 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<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
if ($Gson$Types.getRawType(returnType) != Call.class) {
return null;
}
final Type responseType = getCallResponseType(returnType);
return new CallAdapter<Call<?>>() {
@Override
public Type responseType() {
return responseType;
}
@Override
public <R> Call<R> adapt(Call<R> call) {
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
};
}
static final class ExecutorCallbackCall<T> implements Call<T> {
private final Executor callbackExecutor;
private final Call<T> delegate;
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
@Override
public void enqueue(Callback<T> callback) {
delegate.enqueue(new ExecutorCallback<>(callbackExecutor, callback));
}
@Override
public Response<T> execute() throws IOException {
return delegate.execute();
}
@Override
public void cancel() {
delegate.cancel();
}
@SuppressWarnings("CloneDoesntCallSuperClone") // Performing deep clone.
@Override
public Call<T> clone() {
return new ExecutorCallbackCall<>(callbackExecutor, delegate.clone());
}
}
static final class ExecutorCallback<T> implements Callback<T> {
private final Executor callbackExecutor;
private final Callback<T> delegate;
ExecutorCallback(Executor callbackExecutor, Callback<T> delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
@Override
public void onResponse(final Response<T> 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<Foo> or Call<? extends Foo>");
}
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<T> cannot use Response as its generic parameter. "
+ "Specify the response body type only (e.g., Call<TweetResponse>).");
}
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;
}
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(new ErrorHandlingExecutorCallAdapterFactory(new ErrorHandlingExecutorCallAdapterFactory.MainThreadExecutor()))
.build();
GitHubService service = retrofit.create(GitHubService.class);
// This is RetrofitError converted to Retrofit 2
public class RetrofitException extends RuntimeException {
public static RetrofitException httpError(String url, Response response, Retrofit retrofit) {
String message = response.code() + " " + response.message();
return new RetrofitException(message, url, response, Kind.HTTP, null, retrofit);
}
public static RetrofitException networkError(IOException exception) {
return new RetrofitException(exception.getMessage(), null, null, Kind.NETWORK, exception, null);
}
public static RetrofitException unexpectedError(Throwable exception) {
return new RetrofitException(exception.getMessage(), null, null, Kind.UNEXPECTED, exception, null);
}
/** Identifies the event kind which triggered a {@link RetrofitException}. */
public enum Kind {
/** An {@link IOException} occurred while communicating to the server. */
NETWORK,
/** A non-200 HTTP status code was received from the server. */
HTTP,
/**
* An internal error occurred while attempting to execute a request. It is best practice to
* re-throw this exception so your application crashes.
*/
UNEXPECTED
}
private final String url;
private final Response response;
private final Kind kind;
private final Retrofit retrofit;
RetrofitException(String message, String url, Response response, Kind kind, Throwable exception, Retrofit retrofit) {
super(message, exception);
this.url = url;
this.response = response;
this.kind = kind;
this.retrofit = retrofit;
}
/** The request URL which produced the error. */
public String getUrl() {
return url;
}
/** Response object containing status code, headers, body, etc. */
public Response getResponse() {
return response;
}
/** The event kind which triggered this error. */
public Kind getKind() {
return kind;
}
/** The Retrofit this request was executed on */
public Retrofit getRetrofit() {
return retrofit;
}
/**
* HTTP response body converted to specified {@code type}. {@code null} if there is no
* response.
*
* @throws IOException if unable to convert the body to the specified {@code type}.
*/
public <T> T getErrorBodyAs(Class<T> type) throws IOException {
if (response == null || response.errorBody() == null) {
return null;
}
Converter<ResponseBody, T> converter = retrofit.responseConverter(type, new Annotation[0]);
return converter.convert(response.errorBody());
}
}
@birender-s
Copy link

i am getting various compilation errors especially in ErrorHandlingExecutorCallAdapterFactory. is there working project which i can refer (import classes, libs). Please advise.

@msangel
Copy link

msangel commented Aug 13, 2017

I do adopt this to the latest version of retrofit
https://gist.github.com/msangel/6d1e65c84df273d9b3fbd04b55add72d

@DeevD
Copy link

DeevD commented Nov 30, 2017

i got thoese error
java.lang.IllegalArgumentException: Unable to create call adapter for io.reactivex.Single<comm.comquas.lodggy.data.model.api.UserRegisterModel>

@seyoung-hyun
Copy link

@koesie10
Thanks your code :)
Can I use your code (RetrofitException.java) in my commercial app?
Please let me know how I can use the code for commercial distribution.

@Kiran89kumar
Copy link

Hi any one has written Unit test for call adpater?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment