Повторная попытка запроса с помощью Retrofit 2

Как добавить функцию повтора для запросов, отправленных библиотекой Retrofit 2 . Что-то вроде:

service.listItems().enqueue(new Callback<List<Item>>() { @Override public void onResponse(Response<List<Item>> response) { ... } @Override public void onFailure(Throwable t) { ... } }).retryOnFailure(5 /* times */); 

Solutions Collecting From Web of "Повторная попытка запроса с помощью Retrofit 2"

Я, наконец, сделал что-то вроде этого, для всех, кого это интересует:

1

Сначала я создал абстрактный класс 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); } } 

Используя этот класс, я могу сделать что-то вроде этого:

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

2

Это не совсем удовлетворительно, потому что я должен пройти один и тот же serviceCall дважды. Это может сбивать с толку, поскольку можно подумать, что второй serviceCall (который входит в конструктор CallbackWithRetry ) должен или мог быть чем-то отличным от первого (который мы вызываем на него методом enqueue )

Поэтому я реализовал вспомогательный класс 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); } }); } } 

И я могу использовать его так:

 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. } }); 

С этим я должен передать стандартный метод Callback для метода enqueueWithRetry и это заставляет меня реализовать onFailure (хотя в предыдущем методе я могу реализовать его тоже)

Так вот как я решил проблему. Любые предложения для лучшего дизайна будут оценены.

Пойдите с RxJava Observable и повторите попытку вызова () Doc: https://github.com/ReactiveX/RxJava/wiki/Error-Handling-Operators

Я сделал пользовательскую реализацию интерфейса Callback, вы можете в значительной степени использовать его вместо первоначального обратного вызова. Если вызов успешный, вызывается метод onResponse (). Если после повторной попытки выполнить заданное количество повторений вызов завершается неудачно, вызывается onFailedAfterRetry ().

 public abstract class BackoffCallback<T> implements Callback<T> { private static final int RETRY_COUNT = 3; /** * Base retry delay for exponential backoff, in Milliseconds */ private static final double RETRY_DELAY = 300; private int retryCount = 0; @Override public void onFailure(final Call<T> call, Throwable t) { retryCount++; if (retryCount <= RETRY_COUNT) { int expDelay = (int) (RETRY_DELAY * Math.pow(2, Math.max(0, retryCount - 1))); new Handler().postDelayed(new Runnable() { @Override public void run() { retry(call); } }, expDelay); } else { onFailedAfterRetry(t); } } private void retry(Call<T> call) { call.clone().enqueue(this); } public abstract void onFailedAfterRetry(Throwable t); } 

https://gist.github.com/milechainsaw/811c1b583706da60417ed10d35d2808f

Я сделал что-то очень похожее на Ashkan Sarlak, но так как Retrofit 2.1 передает Call<T> в метод onFailure , вы можете упростить один абстрактный класс CallbackWithRetry<T> . Видеть:

 public abstract class CallbackWithRetry<T> implements Callback<T> { private static final String TAG = "CallbackWithRetry"; private int retryCount = 0; private final Logger logger; private final String requestName; private final int retryAttempts; protected CallbackWithRetry(@NonNull Logger logger, @NonNull String requestName, int retryAttempts) { this.logger = logger; this.requestName = requestName; this.retryAttempts = retryAttempts; } @Override public void onFailure(Call<T> call, Throwable t) { if (retryCount < retryAttempts) { logger.e(TAG, "Retrying ", requestName, "... (", retryCount, " out of ", retryAttempts, ")"); retry(call); retryCount += 1; } else { logger.e(TAG, "Failed request ", requestName, " after ", retryAttempts, " attempts"); } } private void retry(Call<T> call) { call.clone().enqueue(this); } }