Search code examples
javaandroidretrofit

Retrying the request using Retrofit 2


How can I add retry functionality to the requests sent by Retrofit 2 library. Something like:

service.listItems().enqueue(new Callback<List<Item>>() {
        @Override
        public void onResponse(Response<List<Item>> response) {
            ...
        }

        @Override
        public void onFailure(Throwable t) {
            ...
        }
    }).retryOnFailure(5 /* times */);

Solution

  • I finally did something like this, for anyone interested:

    1

    First I made an abstract class CallbackWithRetry

    public abstract class CallbackWithRetry<T> implements Callback<T> {
    
        private static final int TOTAL_RETRIES = 3;
        private static final String TAG = CallbackWithRetry.class.getSimpleName();
        private final Call<T> call;
        private int retryCount = 0;
    
        public CallbackWithRetry(Call<T> call) {
            this.call = call;
        }
    
        @Override
        public void onFailure(Throwable t) {
            Log.e(TAG, t.getLocalizedMessage());
            if (retryCount++ < TOTAL_RETRIES) {
                Log.v(TAG, "Retrying... (" + retryCount + " out of " + TOTAL_RETRIES + ")");
                retry();
            }
        }
    
        private void retry() {
            call.clone().enqueue(this);
        }
    }
    

    Using this class I can do something like this:

    serviceCall.enqueue(new CallbackWithRetry<List<Album>>(serviceCall) {
        @Override
        public void onResponse(Response<List<Album>> response) {
            ...
        }
    });
    

    2

    This is not completely satisfactory because I have to pass same serviceCall twice. This can confusing as one can think the second serviceCall (that goes into constructor of CallbackWithRetry) should or could be something different from first one (which we invoke enqueue method on it)

    So I implemented a helper class CallUtils:

    public class CallUtils {
    
        public static <T> void enqueueWithRetry(Call<T> call, final Callback<T> callback) {
            call.enqueue(new CallbackWithRetry<T>(call) {
                @Override
                public void onResponse(Response<T> response) {
                    callback.onResponse(response);
                }
    
                @Override
                public void onFailure(Throwable t) {
                    super.onFailure(t);
                    callback.onFailure(t);
                }
            });
        }
    
    }
    

    And I can use it like this:

    CallUtils.enqueueWithRetry(serviceCall, new Callback<List<Album>>() {
        @Override
        public void onResponse(Response<List<Album>> response) {
            ...
        }
    
        @Override
        public void onFailure(Throwable t) {
            // Let the underlying method do the job of retrying.
        }
    });
    

    With this I have to pass a standard Callback to enqueueWithRetry method and it makes me implement onFailure (Though in the previous method I can implement it too)

    So this is how I've solved the issue. Any suggestion for a better design would be appreciated.