我正在我的Android项目中使用Retrofit/OkHttp(1.6)。
我没有发现它们中的任何一个内置请求重试机制。在进一步搜索后,我了解到OkHttp似乎具有无声重试功能。但我在我的任何连接(HTTP或HTTPS)上都没有看到这种情况发生。如何使用okclient配置重试?
目前,我正在捕获异常并维护计数器变量进行重试。
我正在我的Android项目中使用Retrofit/OkHttp(1.6)。
我没有发现它们中的任何一个内置请求重试机制。在进一步搜索后,我了解到OkHttp似乎具有无声重试功能。但我在我的任何连接(HTTP或HTTPS)上都没有看到这种情况发生。如何使用okclient配置重试?
目前,我正在捕获异常并维护计数器变量进行重试。
对于Retrofit 2.x:
您可以使用 Call.clone()方法来克隆请求并执行它。
对于Retrofit 1.x:
您可以使用拦截器。创建一个自定义的拦截器。
OkHttpClient client = new OkHttpClient();
client.setConnectTimeout(CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
client.setReadTimeout(READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
client.interceptors().add(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// try the request
Response response = chain.proceed(request);
int tryCount = 0;
while (!response.isSuccessful() && tryCount < 3) {
Log.d("intercept", "Request is not successful - " + tryCount);
tryCount++;
// retry the request
response.close()
response = chain.proceed(request);
}
// otherwise just pass the original response on
return response;
}
});
并在创建RestAdapter时使用它。
new RestAdapter.Builder()
.setEndpoint(API_URL)
.setRequestInterceptor(requestInterceptor)
.setClient(new OkClient(client))
.build()
.create(Adapter.class);
Response response = chain.proceed(request);
这行代码之后的所有代码都将无法执行,因为不会收到任何 Response
。 - Yamashiro Rionresponse.close()
。 - Rajcall.clone
如何使用? - malhobayyebresponse = chain.call().clone().execute();
@malhobayyeb response = chain.call().clone().execute();
- TongChen我不知道这对你是否可行,但你可以使用RxJava和Retrofit一起使用。
Retrofit能够返回rest调用时的Observables。在Observables上,您只需调用retry(count)
即可在Observable发生错误时重新订阅。
您需要像这样在接口中定义调用:
@GET("/data.json")
Observable<DataResponse> fetchSomeData();
那么你可以像这样订阅这个Observable:
restApi.fetchSomeData()
.retry(5) // Retry the call 5 times if it errors
.subscribeOn(Schedulers.io()) // execute the call asynchronously
.observeOn(AndroidSchedulers.mainThread()) // handle the results in the ui thread
.subscribe(onComplete, onError);
// onComplete and onError are of type Action1<DataResponse>, Action1<Throwable>
// Here you can define what to do with the results
我和你有同样的问题,这是我的解决方案。RxJava是与Retrofit结合使用的非常好的库。除了重试之外,您甚至可以做许多很酷的事情(例如 组合和链接调用)。
我认为您不应该将API处理(由retrofit / okhttp完成)与重试混合在一起。重试机制更加正交,可以在许多其他情境中使用。因此,我使用Retrofit/OkHTTP处理所有API调用和请求/响应处理,并在其上方引入另一层来重试API调用。
在我有限的Java经验中,我发现jhlaterman的Failsafe库(GitHub:jhalterman/failsafe)非常适用于清晰地处理许多“重试”情况。例如,这是我如何将其与一个已实例化的mySimpleService retrofit一起用于身份验证:
AuthenticationResponse authResp = Failsafe.with(
new RetryPolicy().retryOn(Arrays.asList(IOException.class, AssertionError.class))
.withBackoff(30, 500, TimeUnit.MILLISECONDS)
.withMaxRetries(3))
.onRetry((error) -> logger.warn("Retrying after error: " + error.getMessage()))
.get(() -> {
AuthenticationResponse r = mySimpleAPIService.authenticate(
new AuthenticationRequest(username,password))
.execute()
.body();
assert r != null;
return r;
});
上述代码捕获套接字异常、连接错误、断言失败,并最多重试3次,采用指数退避。它还允许您自定义在重试时的行为,并允许您指定备选方案。它非常可配置,并且可以适应大多数重试情况。 随意查阅该库的文档,因为除了重试之外,它还提供许多其他好处。2.3.1
似乎需要 API 26,因为使用了时间单位(ChronoUnit)。 - behelitresponse.isSuccessful()存在的问题在于当您遇到像SocketTimeoutException这样的异常时。
我修改了原始代码以解决此问题。
OkHttpClient client = new OkHttpClient();
client.setConnectTimeout(CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
client.setReadTimeout(READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
client.interceptors().add(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = null;
boolean responseOK = false;
int tryCount = 0;
while (!responseOK && tryCount < 3) {
try {
response = chain.proceed(request);
responseOK = response.isSuccessful();
}catch (Exception e){
Log.d("intercept", "Request is not successful - " + tryCount);
}finally{
tryCount++;
}
}
// otherwise just pass the original response on
return response;
}
});
希望能有所帮助。 敬礼。
return response;
替换为return response != null ? response : chain.proceed(request);
- oxied感谢最佳答案的提供,这是对我本人有用的。如果在连接时出现问题,最好等待几秒钟后再重试。
public class ErrorInterceptor implements Interceptor {
ICacheManager cacheManager;
Response response = null;
int tryCount = 0;
int maxLimit = 3;
int waitThreshold = 5000;
@Inject
public ErrorInterceptor() {
}
@Override
public Response intercept(Chain chain){
// String language = cacheManager.readPreference(PreferenceKeys.LANGUAGE_CODE);
Request request = chain.request();
response = sendReqeust(chain,request);
while (response ==null && tryCount < maxLimit) {
Log.d("intercept", "Request failed - " + tryCount);
tryCount++;
try {
Thread.sleep(waitThreshold); // force wait the network thread for 5 seconds
} catch (InterruptedException e) {
e.printStackTrace();
}
response = sendReqeust(chain,request);
}
return response;
}
private Response sendReqeust(Chain chain, Request request){
try {
response = chain.proceed(request);
if(!response.isSuccessful())
return null;
else
return response;
} catch (IOException e) {
return null;
}
}
}
以下是一个对我有效的解决方案,适用于OkHttp 3.9.1(考虑到其他回答此问题的方法):
@NonNull
@Override
public Response intercept(@NonNull Chain chain) throws IOException {
Request request = chain.request();
int retriesCount = 0;
Response response = null;
do {
try {
response = chain.proceed(request);
// Retry if no internet connection.
} catch (ConnectException e) {
Log.e(TAG, "intercept: ", e);
retriesCount++;
try {
Thread.sleep(RETRY_TIME);
} catch (InterruptedException e1) {
Log.e(TAG, "intercept: ", e1);
}
}
} while (response == null && retriesCount < MAX_RETRIES);
// If there was no internet connection, then response will be null.
// Need to initialize response anyway to avoid NullPointerException.
if (response == null) {
response = chain.proceed(newRequest);
}
return response;
}
对于那些更喜欢使用拦截器处理重试问题的人 - 基于Sinan的答案,这是我提出的拦截器,它包括重试计数和退避延迟,并且只在网络可用且请求未被取消时重试尝试。 (仅处理IOException(SocketTimeout,UnknownHost等))
builder.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// try the request
Response response = null;
int tryCount = 1;
while (tryCount <= MAX_TRY_COUNT) {
try {
response = chain.proceed(request);
break;
} catch (Exception e) {
if (!NetworkUtils.isNetworkAvailable()) {
// if no internet, dont bother retrying request
throw e;
}
if ("Canceled".equalsIgnoreCase(e.getMessage())) {
// Request canceled, do not retry
throw e;
}
if (tryCount >= MAX_TRY_COUNT) {
// max retry count reached, giving up
throw e;
}
try {
// sleep delay * try count (e.g. 1st retry after 3000ms, 2nd after 6000ms, etc.)
Thread.sleep(RETRY_BACKOFF_DELAY * tryCount);
} catch (InterruptedException e1) {
throw new RuntimeException(e1);
}
tryCount++;
}
}
// otherwise just pass the original response on
return response;
}
});
import retrofit2.Call;
import retrofit2.CallAdapter;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.HttpException;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.jackson.JacksonConverterFactory;
import rx.Observable;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
那么
RxJavaCallAdapterFactory originCallAdaptorFactory = RxJavaCallAdapterFactory.create();
CallAdapter.Factory newCallAdaptorFactory = new CallAdapter.Factory() {
@Override
public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
CallAdapter<?> ca = originCallAdaptorFactory.get(returnType, annotations, retrofit);
return new CallAdapter<Observable<?>>() {
@Override
public Type responseType() {
return ca.responseType();
}
int restRetryCount = 3;
@Override
public <R> Observable<?> adapt(Call<R> call) {
Observable<?> rx = (Observable<?>) ca.adapt(call);
return rx.retryWhen(errors -> errors.flatMap(error -> {
boolean needRetry = false;
if (restRetryCount >= 1) {
if (error instanceof IOException) {
needRetry = true;
} else if (error instanceof HttpException) {
if (((HttpException) error).code() != 200) {
needRetry = true;
}
}
}
if (needRetry) {
restRetryCount--;
return Observable.just(null);
} else {
return Observable.error(error);
}
}));
}
};
}
};
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
使用
.addCallAdapterFactory(newCallAdaptorFactory)
例如:
return new Retrofit
.Builder()
.baseUrl(baseUrl)
.client(okClient)
.addCallAdapterFactory(newCallAdaptorFactory)
.addConverterFactory(JacksonConverterFactory.create(objectMapper));
rx.retryWhen
将不会被调用。如果您坚持检查这样的响应,则可以在.retryWhen之前添加rx.subscribeOn(...抛出错误...
。public class RetryWithDelayOrInternet implements Function<Flowable<? extends Throwable>, Flowable<?>> {
public static boolean isInternetUp;
private int retryCount;
@Override
public Flowable<?> apply(final Flowable<? extends Throwable> attempts) {
return Flowable.fromPublisher(s -> {
while (true) {
retryCount++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
attempts.subscribe(s);
break;
}
if (isInternetUp || retryCount == 15) {
retryCount = 0;
s.onNext(new Object());
}
}
})
.subscribeOn(Schedulers.single());
}}
在使用.subscribe之前,您应该像这样使用它:
.retryWhen(new RetryWithDelayOrInternet())
你应该手动更改isInternetUp字段
public class InternetConnectionReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
boolean networkAvailable = isNetworkAvailable(context);
RetryWithDelayOrInternet.isInternetUp = networkAvailable;
}
public static boolean isNetworkAvailable(Context context) {
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}}
final Observable<List<NewsDatum>> newsDetailsObservable = apiService.getCandidateNewsItem(newsId).map((newsDetailsParseObject) -> {
return newsDetailsParseObject;
});
newsDetailsObservable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.retry((integer, throwable) -> {
//MAX_NUMBER_TRY is your maximum try number
if(integer <= MAX_NUMBER_TRY){
return true;//this will retry the observable (request)
}
return false;//this will not retry and it will go inside onError method
})
.subscribe(new Subscriber<List<NewsDatum>>() {
@Override
public void onCompleted() {
// do nothing
}
@Override
public void onError(Throwable e) {
//do something with the error
}
@Override
public void onNext(List<NewsDatum> apiNewsDatum) {
//do something with the parsed data
}
});
apiService是我的RetrofitServiceProvider对象。
顺便说一下:我正在使用Java 8,因此代码中有很多lambda表达式。