最近在一个项目需求中,涉及到网络请求能有重试机制,而我们项目中网络请求使用的是Retrofit框架,以前也没做过这种,老大提示了下说可以用拦截器Interceptor或者封装Callback实现。
自定义Interceptor
废话少说,直接上代码
public class OkHttpRetryInterceptor implements Interceptor{
private int mMaxRetryCount;
private long mRetryInterval;
public OkHttpRetryInterceptor(int maxRetryCount, long retryInterval) {
mMaxRetryCount = maxRetryCount;
mRetryInterval = retryInterval;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = doRequest(chain, request);
int retryNum = 1;
while(((response==null)||response.isSuccessful())&&retryNum<=mMaxRetryCount){
try {
Thread.sleep(mRetryInterval);
} catch (InterruptedException e) {
e.printStackTrace();
}C
retryNum++;
response = doRequest(chain, request);
}
return response;
}
private Response doRequest(Chain chain, Request request) {
try {
return chain.proceed(request);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static class Builder {
private int mRetryCount = 1;
private long mRetryInterval = 1000;
public Builder buildRetryCount(int retryCount){
this.mRetryCount = retryCount;
return this;
}
public Builder buildRetryInterval(long retryInterval){
this.mRetryInterval = retryInterval;
return this;
}
public OkHttpRetryInterceptor build(){
return new OkHttpRetryInterceptor(mRetryCount,mRetryInterval);
}
}
}
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new OkHttpRetryInterceptor.Builder()
.buildRetryCount(retryCount)
.buildRetryInterval(retryInterval)
.build())
.build();
思路解释:
- 自定义了一个OkHttpRetryInterceptor类,实现Interceptor接口
- 定义mMaxRetryCount ,mRetryInterval 两个成员变量用于保存最大重试次数和重试间隔,并利用Build模式对外提供自定义这两个变量的接口。
- 重试机制最核心的部分是实现了Interceptor接口的intercept(Chain chain)方法,该方法提供了一个Chain类型的对象,Chain对象中可以获取到Request对象,而调用Chain对象的proceed方法(该方法接收一个Request对象)就可发起一次网络请求,该方法返回Response对象。
- 通过上述返回的Response对象可判断请求结果,如果失败则利用Thread.sleep相应的间隔时间之后,再调用Chain对象的proceed方法再发起一次,如此循环到请求成功或最大重试次数之后返回。
- 最后在OkHttpClient的对象构造时加入自定义的OkHttpRetryInterceptor即实现了重试机制。
封装Callback
public abstract class RetryCallback<T> implements Callback<T> {
private static final String TAG = RetryCallback.class.getSimpleName();
private int mRetryCount;
private long mRetryInterval;
private int mCurrentRetryCount;
private boolean isExecuting;
private Call<T> mCall;
private Timer timer = new Timer();
public RetryCallback(Call<T> call, int retryCount, long retryInterval) {
isExecuting = true;
mCall = call;
mRetryCount = retryCount;
mRetryInterval = retryInterval;
}
@Override
public final void onResponse(@NonNull Call<T> call, @NonNull Response<T> response) {
MLog.i(TAG,"onResponse");
isExecuting = false;
if (!response.isSuccessful() && mCurrentRetryCount < mRetryCount) {
mCurrentRetryCount++;
retryRequest(call);
} else {
onRequestResponse(call, response);
}
}
@Override
public final void onFailure(@NonNull Call<T> call, @NonNull Throwable t) {
MLog.i(TAG,"onFailure");
isExecuting = false;
if (mCurrentRetryCount < mRetryCount) {
mCurrentRetryCount++;
retryRequest(call);
} else {
onRequestFail(call, t);
}
}
private void retryRequest(final Call<T> call) {
MLog.i(TAG,"retryRequest");
onStartRetry();
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
synchronized (RetryCallback.this){
mCall = call.clone();
mCall.enqueue(RetryCallback.this);
isExecuting = true;
}
}
};
timer.schedule(timerTask, mRetryInterval);
}
public void cancelCall(){
synchronized (this){
if (!isExecuting){
timer.cancel();
}else {
mCall.cancel();
}
}
}
public abstract void onRequestResponse(Call call, Response response);
public abstract void onRequestFail(Call call, Throwable t);
public abstract void onStartRetry();
}
这种方式是基于在Retrofit的异步请求方法enqueue中,需要传入的一个Callback类型的对象参数。
在Callback接口中有两个方法,onResponse和onFailure。在这两个方法中都能拿到一个Call类型的对象,该对象其实就是最开始发起请求时构造的Call对象,用以再次发起网络请求,这就是该方式实现重试机制的基础。
当通过Callback的两个回调接口判断请求失败时,则利用Timer延时相应的重试间隔,这里需要注意,因为一个Call对象只能使用一次,回调中拿到的Call对象是前一次请求使用过的,于是就需要调用Call的clone方法克隆出一个相同的Call对象。再调用Call的enqueue方法发起请求,这样就实现了重试机制。