Java 使用 Retrofit 2 重试请求

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32579754/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 12:48:11  来源:igfitidea点击:

Retrying the request using Retrofit 2

javaandroidretrofit

提问by Ashkan Sarlak

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

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

采纳答案by Ashkan Sarlak

I finally did something like this, for anyone interested:

我终于做了这样的事情,对于任何感兴趣的人:

1

1

First I made an abstract class CallbackWithRetry

首先我做了一个抽象类 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

2

This is not completely satisfactory because I have to pass same serviceCalltwice. 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 enqueuemethod on it)

这并不完全令人满意,因为我必须通过相同的serviceCall两次。这可能会令人困惑,因为人们会认为第二个serviceCall(进入 的构造函数CallbackWithRetry)应该或可能与第一个(我们对其调用enqueue方法)有所不同

So I implemented a helper class CallUtils:

所以我实现了一个助手类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 Callbackto enqueueWithRetrymethod and it makes me implement onFailure(Though in the previous method I can implement it too)

有了这个,我必须要通过一个标准CallbackenqueueWithRetry法,这让我实现onFailure(尽管在前面的方法我可以实现它太)

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

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

回答by guillaume_fr

Go with RxJava Observable and call retry() Doc: https://github.com/ReactiveX/RxJava/wiki/Error-Handling-Operators

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

回答by milechainsaw

I've made custom implementation of the Callback interface, you can pretty much use it in place of original callback. If call is successful, the onResponse() method is called. If after retrying for set amount of repetitions call fails, onFailedAfterRetry() is called.

我已经对 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

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

回答by huwr

I did something quite similar to Ashkan Sarlak, but since Retrofit 2.1 passes the Call<T>into the onFailuremethod, you can simplify to one CallbackWithRetry<T>abstract class. See:

我做了一些与 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);
  }
}

回答by Radesh

ashkan-sarlakanswer work great and i'm just try to make it up to date.

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

From retrofit 2.1

改造 2.1

onFailure(Throwable t) 

Change to

改成

onFailure(Call<T> call, Throwable t)

So this make it so easy now.just create CallbackWithRetry.javalike this

所以这让它现在变得如此简单。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);
    }
}

That's all! you can simply use it like 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
        }
    });

回答by SIVAKUMAR.J

I think for android we no need to go for retrofit for this.We can make use of Workmanager (which predefine android api). We can use "ListenableWorker.Result.SUCCESS","ListenableWorker.Result.RETRY" ,etc and achieve the above goals.

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

回答by DTodt

With Retrofit 2.5

使用改造 2.5

Now it's possible to make async sync calls through java.util.concurrent.CompletableFuture, the code waits for it's completion wich is very nice.

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

Here's a gistwith a working solution.

这是一个带有工作解决方案的要点