Skip to content

Instantly share code, notes, and snippets.

@naturalwarren
Last active July 28, 2022 10:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save naturalwarren/d1a053707fa1b9987c1b0ff12f399d23 to your computer and use it in GitHub Desktop.
Save naturalwarren/d1a053707fa1b9987c1b0ff12f399d23 to your computer and use it in GitHub Desktop.
Retrofit 2 CallAdapterFactory that Wraps Network Errors
package com.uber.retrofit2.adapters.network.exception;
import android.support.annotation.NonNull;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Request;
/**
* Exception for unexpected, network errors.
*/
public class NetworkException extends IOException {
private final Request request;
private final IOException ioException;
public NetworkException(@NonNull Request request, @NonNull IOException ioException) {
super(ioException.getMessage(), ioException.getCause());
this.request = request;
this.ioException = ioException;
}
/**
* @return the {@link IOException} returned by {@link Call#execute()} or {@link Call#enqueue(Callback)}.
*/
@NonNull
public IOException ioException() {
return ioException;
}
/**
* @return the {@link Request} that resulted in an error at the network level.
*/
@NonNull
public Request request() {
return request;
}
}
package com.uber.retrofit2.adapters.network.exception;
import android.support.annotation.NonNull;
import java.io.IOException;
import okhttp3.Request;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
/**
* An {@link Call} that delegates to a backing Call and returns {@link NetworkException} when an {@link IOException} is
* encountered.
* @param <T> Successful response body type.
*/
final class NetworkExceptionCall<T> implements Call<T> {
private final Call<T> delegateCall;
NetworkExceptionCall(@NonNull Call<T> delegateCall) {
this.delegateCall = delegateCall;
}
@Override
public Response<T> execute() throws IOException {
try {
return delegateCall.execute();
} catch (IOException e) {
throw new NetworkException(request(), e);
}
}
@Override
public void enqueue(final Callback<T> callback) {
delegateCall.enqueue(new Callback<T>() {
@Override
public void onResponse(Call<T> call, Response<T> response) {
callback.onResponse(call, response);
}
@Override
public void onFailure(Call<T> call, Throwable t) {
if (t instanceof IOException) {
callback.onFailure(call, new NetworkException(request(), (IOException) t));
} else {
callback.onFailure(call, t);
}
}
});
}
@Override
public boolean isExecuted() {
return delegateCall.isExecuted();
}
@Override
public void cancel() {
delegateCall.cancel();
}
@Override
public boolean isCanceled() {
return delegateCall.isCanceled();
}
// We are a final type & this saves clearing state.
@SuppressWarnings("CloneDoesntCallSuperClone")
@Override
public Call<T> clone() {
return new NetworkExceptionCall<>(delegateCall.clone());
}
@Override
public Request request() {
return delegateCall.request();
}
}
package com.uber.retrofit2.adapters.network.exception;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import okhttp3.Request;
import retrofit2.Call;
import retrofit2.CallAdapter;
import retrofit2.Callback;
import retrofit2.Retrofit;
/**
* A {@linkplain CallAdapter.Factory call adapter} that wraps {@link IOException} with its {@link Request}
* in {@link NetworkException}.
*
* NOTE: This is a forwarding adapter and delegates to another call adapter. It must be registered to your
* {@link Retrofit} instance before the adapter it delegates to.
*/
public final class NetworkExceptionCallAdapterFactory extends CallAdapter.Factory {
/**
* @return an instance which will wrap any {@link IOException} with its corresponding {@link Request}.
*/
public static NetworkExceptionCallAdapterFactory create() {
return new NetworkExceptionCallAdapterFactory();
}
@Override
public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
return new NetworkExceptionCallAdapter(retrofit.nextCallAdapter(this, returnType, annotations));
}
/**
* Wraps a delegate call adapter and passes on a {@link NetworkExceptionCall} which performs
* {@link IOException} wrapping when an error is encountered upon invoking
* {@link NetworkExceptionCall#enqueue(Callback)} or {@link NetworkExceptionCall#execute()}.
*/
private static final class NetworkExceptionCallAdapter implements CallAdapter<Object> {
private final CallAdapter<?> delegateCallAdapter;
NetworkExceptionCallAdapter(CallAdapter<?> delegateCallAdapter) {
this.delegateCallAdapter = delegateCallAdapter;
}
@Override
public Type responseType() {
return delegateCallAdapter.responseType();
}
@Override
public <R> Object adapt(Call<R> call) {
return delegateCallAdapter.adapt(new NetworkExceptionCall<>(call));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment