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()); | |
} | |
} |
This comment has been minimized.
This comment has been minimized.
i am getting various compilation errors especially in ErrorHandlingExecutorCallAdapterFactory. is there working project which i can refer (import classes, libs). Please advise. |
This comment has been minimized.
This comment has been minimized.
I do adopt this to the latest version of retrofit |
This comment has been minimized.
This comment has been minimized.
i got thoese error |
This comment has been minimized.
This comment has been minimized.
@koesie10 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
https://gist.github.com/koesie10/bc6c62520401cc7c858f#file-retrofitexception-java-L72
responseConverter is now responseBodyConverter.
https://github.com/square/retrofit/blob/master/retrofit/src/main/java/retrofit2/Retrofit.java#L307