【问题标题】:Retrying the request using Retrofit 2使用 Retrofit 2 重试请求
【发布时间】:2015-12-11 08:05:53
【问题描述】:

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

【问题讨论】:

    标签: java android retrofit


    【解决方案1】:

    使用 RxJava Observable 并调用 retry() 文档:https://github.com/ReactiveX/RxJava/wiki/Error-Handling-Operators

    【讨论】:

      【解决方案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(虽然在前面的方法中我也可以实现它)

      这就是我解决问题的方法。任何关于更好设计的建议将不胜感激。

      【讨论】:

      • 在 CallUtils 上,被覆盖的方法应该是:@Override public void onResponse(Response response, Retrofit retrofit) { callback.onResponse(response, retrofit); }
      • 我们如何从 CallbackWithRetry 类中更改 baseUrl?
      • 很好的答案!顺便说一句,在 Retrofit 2.1 中,Callback.onFailure(Call&lt;T&gt; call, Throwable t) 已经有了“呼叫”,不再需要CallUtils
      【解决方案3】:

      我已经对回调接口进行了自定义实现,您几乎可以使用它来代替原始回调。如果调用成功,则调用 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

      【讨论】:

      • 在我的自定义 CallAdapter.Factory 中实施此解决方案会导致 RuntimeException(无法在未调用 Looper.prepare() 的线程内创建处理程序)
      【解决方案4】:

      我做了一些与 Ashkan Sarlak 非常相似的事情,但由于 Retrofit 2.1 将 Call&lt;T&gt; 传递给 onFailure 方法,您可以简化为一个 CallbackWithRetry&lt;T&gt; 抽象类。见:

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

      【讨论】:

      • 这个怎么用?
      • 没试过 - 但你会调用 "call.enque(new CallBackWithRetry() { ... }" 匿名内部类继承了 "onFailure" 的默认实现
      【解决方案5】:

      ashkan-sarlak 回答效果很好,我只是尝试使其保持最新状态。

      来自retrofit 2.1

      onFailure(Throwable t) 
      

      改成

      onFailure(Call<T> call, Throwable t)
      

      所以现在变得如此简单。只需像这样创建CallbackWithRetry.java

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

      就是这样!你可以像这样简单地使用它

      call.enqueue(new CallbackWithRetry<someResponseClass>() {
      
              @Override
              public void onResponse(@NonNull Call<someResponseClass> call, @NonNull retrofit2.Response<someResponseClass> response) {
                  //do what you want
              }
              @Override
              public void onFailure(@NonNull Call<someResponseClass> call, @NonNull Throwable t) {
                  super.onFailure(call,t);
                  //do some thing to show ui you trying
                  //or don't show! its optional
              }
          });
      

      【讨论】:

        【解决方案6】:

        我认为对于 android,我们不需要为此进行改造。我们可以使用 Workmanager(预定义 android api)。 我们可以使用“ListenableWorker.Result.SUCCESS”、“ListenableWorker.Result.RETRY”等来实现上述目标。

        【讨论】:

          【解决方案7】:

          使用改造 2.5

          现在可以通过 java.util.concurrent.CompletableFuture 进行异步同步调用,代码等待它的完成非常好。

          这里有一个gist 有一个可行的解决方案。

          【讨论】:

            猜你喜欢
            • 2014-08-25
            • 2017-05-30
            • 2018-03-29
            • 1970-01-01
            • 2015-12-12
            • 1970-01-01
            • 1970-01-01
            • 2019-05-18
            • 1970-01-01
            相关资源
            最近更新 更多