Ashkan Sarlak Ashkan Sarlak - 4 months ago 34
Android Question

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 */);

Answer

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.