如何为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 */);
答案 0 :(得分:51)
对于任何有兴趣的人,我终于做了这样的事情:
首先我制作了一个抽象类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) {
...
}
});
这并不完全令人满意,因为我必须两次传递相同的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
(虽然在之前的方法中我也可以实现它)
所以这就是我解决这个问题的方法。任何建议更好的设计将不胜感激。
答案 1 :(得分:9)
我已经自定义了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
答案 2 :(得分:3)
使用RxJava Observable并调用retry() Doc:https://github.com/ReactiveX/RxJava/wiki/Error-Handling-Operators
答案 3 :(得分:3)
我做了一些与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);
}
}
答案 4 :(得分:1)
ashkan-sarlak的答案很好,我只是尝试使其保持最新。
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
}
});
答案 5 :(得分:0)
我认为,对于android,我们不需要为此进行改装。我们可以使用Workmanager(它预定义了android API)。 我们可以使用“ ListenableWorker.Result.SUCCESS”,“ ListenableWorker.Result.RETRY”等来实现上述目标。
答案 6 :(得分:0)