使用Retrofit 2重试请求

Ash*_*lak 27 java android retrofit

如何为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 */);
Run Code Online (Sandbox Code Playgroud)

Ash*_*lak 55

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

使用这个类我可以这样做:

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

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

}
Run Code Online (Sandbox Code Playgroud)

我可以像这样使用它:

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.
    }
});
Run Code Online (Sandbox Code Playgroud)

有了这个我必须将标准传递CallbackenqueueWithRetry方法,它让我实现onFailure(虽然在以前的方法我也可以实现它)

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

  • 很棒的答案!顺便说一下,使用Retrofit 2.1,`Callback.onFailure(Call <T>调用,Throwable t)`已经有了"call",不再需要`CallUtils`了. (3认同)

mil*_*saw 12

我已经自定义了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);

}
Run Code Online (Sandbox Code Playgroud)

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


Rad*_*esh 5

ashkan-sarlak 的答案效果很好,我只是尽力使其更新。

改造2.1开始

onFailure(Throwable t) 
Run Code Online (Sandbox Code Playgroud)

改成

onFailure(Call<T> call, Throwable t)
Run Code Online (Sandbox Code Playgroud)

所以现在变得如此简单。只需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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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
        }
    });
Run Code Online (Sandbox Code Playgroud)