如何使用OkHttp/Retrofit重试HTTP请求?

Jag*_*uar 59 android retrofit okhttp

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

我没有找到内置于其中任何一个的任何请求重试机制.在搜索更多时,我看到OkHttp似乎有沉默重试.我没有看到我的任何连接(HTTP或HTTPS)发生这种情况.如何使用okclient配置重试?

现在,我正在捕获异常并重试维护计数器变量.

Sin*_*zak 69

对于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 = chain.proceed(request);
            }

            // otherwise just pass the original response on
            return response;
        }
    });
Run Code Online (Sandbox Code Playgroud)

并在创建RestAdapter时使用它.

new RestAdapter.Builder()
        .setEndpoint(API_URL)
        .setRequestInterceptor(requestInterceptor)
        .setClient(new OkClient(client))
        .build()
        .create(Adapter.class);
Run Code Online (Sandbox Code Playgroud)

对于Retrofit 2.x;

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

  • 但是有没有办法计算重试次数?Clone 只是复制调用,以便它可以再次执行,但它不算数。 (2认同)
  • 您需要在调用重试之前执行`response.close()` (2认同)
  • @malhobayyeb `响应 = chain.call().clone().execute();` (2认同)

Jon*_*hke 38

我不知道这是否适合您,但您可以将RxJava与Retrofit一起使用.

Retrofit能够在休息时调用Observables.在Oberservables上,您可以retry(count)在发出错误时调用重新订阅Observable.

您必须在界面中定义调用,如下所示:

@GET("/data.json")
Observable<DataResponse> fetchSomeData();
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样订阅这个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
Run Code Online (Sandbox Code Playgroud)

我遇到了和你一样的问题,这实际上是我的解决方案.RxJava是一个非常好的库,可与Retrofit结合使用.除了重试之外,你甚至可以做很多很酷的事情(例如编写和链接调用).

  • 你试过这个吗?似乎在Retrofit Observable上调用retry()(或者只是再次订阅)实际上并不会再次执行请求. (8认同)

San*_* SR 10

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

希望能帮助到你.问候.

  • Internet不可用时崩溃。我正在尝试通过SSL请求,添加了自定义标头并添加了另一个日志记录拦截器。 (2认同)
  • 如果我们有超时或没有连接,则返回null,然后生成NullPointerException (2认同)

Shr*_*yas 9

我认为您不应该将API处理(通过Retrofit / okhttp完成)与重试混合使用。重试机制更加正交,也可以在许多其他上下文中使用。因此,我将Retrofit / OkHTTP用于所有API调用和请求/响应处理,并在上面引入另一层以重试API调用。

到目前为止,以我有限的Java经验,我发现jhlaterman的Failsafe库(github:jhalterman / failsafe)是一个非常通用的库,可以干净地处理许多“重试”情况。举例来说,以下是我如何将其与经过实例化的mySimpleService进行改造的示例,以进行身份​​验证-

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

上面的代码捕获套接字异常,连接错误,断言失败,并对它们进行最多3次重试,并以指数补偿。它还允许您自定义重试行为,还可以指定回退。它是可配置的,并且可以适应大多数重试情况。

可以随意检查该库的文档,因为它提供了许多其他功能,而不仅仅是重试。

  • 从设计的角度来看具有指导意义,指出API调用+处理与重试调用的正交性-一个位于另一个级别上。 (3认同)