如何重试HTTP请求与OkHttp / Retrofit?

我在我的Android项目中使用Retrofit / OkHttp(1.6)。

我没有发现任何内置任何请求重试机制。 在search更多,我读OkHttp似乎有沉默重试。 我没有看到发生在我的任何连接(HTTP或HTTPS)上。 如何使用okclientconfiguration重试?

现在,我正在捕捉exception并重试维护一个计数器variables。

对于Retrofit 1.x;

你可以使用拦截器 。 创build一个自定义拦截器

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 = chain.proceed(request); } // otherwise just pass the original response on return response; } }); 

并在创buildRestAdapter时使用它。

 new RestAdapter.Builder() .setEndpoint(API_URL) .setRequestInterceptor(requestInterceptor) .setClient(new OkClient(client)) .build() .create(Adapter.class); 

对于改造2.x;

您可以使用Call.clone()方法克隆请求并执行它。

我不知道这是否是您的select,但您可以将RxJava与Retrofit一起使用。

改造可以在rest时返回Observables。 在Oberservables上,当它发出一个错误时,你可以调用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 

我有和你一样的问题,这实际上是我的解决scheme。 RxJava是一个非常好的库,可以与Retrofit结合使用。 除了重试之外,你甚至可以做很多很酷的事情(比如组合和链接调用 )。

response.isSuccessful()的问题是当你有一个exception像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; } }); 

希望能帮助到你。 问候。

当发现http连接失败时,我发现由Sinan Kozak提供的方式(OKHttpClient intercepter)不起作用,没有任何关于HTTP响应的东西。

所以我使用另一种方法钩住Observable对象,调用.retryWhen。 另外,我已经添加了retryCount限制。

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

然后添加或replace

 .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 

 .addCallAdapterFactory(newCallAdaptorFactory) 

例如:

 return new Retrofit .Builder() .baseUrl(baseUrl) .client(okClient) .addCallAdapterFactory(newCallAdaptorFactory) .addConverterFactory(JacksonConverterFactory.create(objectMapper)); 

注意:为了简单起见,我只是把HTTP代码> 404代码作为重试,请自己修改。

此外,如果http响应是200,那么上面的rx.retryWhen将不会被调用,如果你坚持检查这样的响应,你可以添加rx.subscribeOn(...throw error... .retryWhen之前。

礼貌的顶部答案,这是什么对我有用。 如果有连接问题,最好再等几秒钟才能重试。

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

}

看来它将在API规范的2.0版本中出现: https : //github.com/square/retrofit/issues/297 。 目前,最好的办法似乎是捕获exception,并手动重试。

正如在文档中所述,更好的可能是在authentication器中使用烘焙,例如:private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception { client.setAuthenticator(new Authenticator() { @Override public Request authenticate(Proxy proxy, Response response) { System.out.println("Authenticating for response: " + response); System.out.println("Challenges: " + response.challenges()); String credential = Credentials.basic("jesse", "password1"); return response.request().newBuilder() .header("Authorization", credential) .build(); } @Override public Request authenticateProxy(Proxy proxy, Response response) { return null; // Null indicates no attempt to authenticate. } }); Request request = new Request.Builder() .url("http://publicobject.com/secrets/hellosecret.txt") .build(); Response response = client.newCall(request).execute(); if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); System.out.println(response.body().string()); } 

我已经玩了很多这个问题,试图find如何重试改造请求的最佳方式。 我正在使用Retrofit 2,所以我的解决scheme是用于Retrofit 2.对于Retrofit 1,您必须使用Interceptor,就像这里接受的答案一样。 @joluet的答案是正确的,但是他没有提到在.subscribe(onComplete,onError)方法之前需要调用重试方法。 这非常重要,否则请求将不会像@joluet回答中提到的@pocmo一样重试。 这是我的例子:

 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,所以很多lambdaexpression式都在代码中。

对于那些喜欢拦截器来处理重试问题的人 – build立在思南的答案上,这里是我提出的拦截器,它包括重试计数和退避延迟,只有当networking可用时重试,当请求不是取消。 (只处理IOExceptions(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 (eg 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; } });