【问题标题】:How to change timeout for a request in okhttp如何在 okhttp 中更改请求的超时时间
【发布时间】:2021-02-10 12:41:33
【问题描述】:

一般来说,我们为 okHttp 客户端设置超时,并且我们使用该客户端的单个实例。因此,一旦生成该客户端,我们就无法更改它的超时时间。

如何更改特定请求的超时时间?到底有没有 在不创建新客户的情况下做到这一点?

某些调用需要更多时间(每个应用至少 1/2)是很常见的,这比其他调用需要更多的超时时间。如果请求可以覆盖默认超时,那就太好了。

【问题讨论】:

    标签: okhttp3 okhttp


    【解决方案1】:

    更新到 Retrofit 2.5.0 你可以使用Invocation

    新:调用类提供对被调用方法和参数列表的引用,作为底层 OkHttp 调用的标记。这可以从 OkHttp 拦截器访问,以进行日志记录、分析或指标聚合等操作。

    这样,您可以创建一个 SpecificTimeout 注释,仅用于您需要设置不同超时持续时间的请求,并在 OkHttp Interceptor 上读取其值以更改此时的超时。

    自定义注释

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface SpecificTimeout {
        int duration();
        TimeUnit unit();
    }
    

    服务接口

    public interface GitHubService {
        @GET("/users")
        @SpecificTimeout(duration = 5000, unit = TimeUnit.MILLISECONDS)
        Call<List<User>> listUsers();
    
        @GET("/repos")
        @SpecificTimeout(duration = 15000, unit = TimeUnit.MILLISECONDS)
        Call<List<Repo>> listRepos();
    }
    

    拦截器

    class TimeoutInterceptor implements Interceptor {
    
        @NonNull
        @Override
        public Response intercept(@NonNull Chain chain) throws IOException {
            Request request = chain.request();
    
            final Invocation tag = request.tag(Invocation.class);
            final Method method = tag != null ? tag.method() : null;
            final SpecificTimeout timeout = method != null ? method.getAnnotation(SpecificTimeout.class) : null;
    
            if (timeout != null) {
               return chain.withReadTimeout(timeout.duration(), timeout.unit())
                           .withConnectTimeout(timeout.duration(), timeout.unit())
                           .withWriteTimeout(timeout.duration(), timeout.unit())
                           .proceed(request);
            }
    
            return chain.proceed(request);
        }
    }
    

    OkHttp 生成器

    OkHttpClient okHttpClient = new OkHttpClient.Builder()
                    //default timeout for not annotated requests
                    .readTimeout(10000, TimeUnit.MILLISECONDS)
                    .connectTimeout(10000, TimeUnit.MILLISECONDS)
                    .writeTimeout(10000, TimeUnit.MILLISECONDS)
                    .addInterceptor(new TimeoutInterceptor())
                    .build();
    

    【讨论】:

    • 很棒的答案。谢谢!
    • 我无法完成这项工作,因为我在 SpecificTimeout 的注释上遇到了 RetentionPolicy 和 ElementType 错误。
    • 为了让它在当前的 Kotlin 版本中工作,我不得不将 @Retention@Target 注释更改为:@Retention(AnnotationRetention.RUNTIME)@Target(AnnotationTarget.FUNCTION)
    【解决方案2】:

    参见https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/PerCallSettings.java,它创建了只修改某些设置的浅层克隆。

    // Copy to customize OkHttp for this request.
    OkHttpClient client2 = client.newBuilder()
        .readTimeout(3000, TimeUnit.MILLISECONDS)
        .build();
    

    【讨论】:

      【解决方案3】:

      在 3.9 中,可以在拦截器中为每个请求设置此项

      https://github.com/square/okhttp/blob/36bd68aa3e93affb12504cd40454e64c6812019c/okhttp-tests/src/test/java/okhttp3/InterceptorTest.java#L747-L757

        @Test public void chainWithReadTimeout() throws Exception {
          Interceptor interceptor1 = new Interceptor() {
            @Override public Response intercept(Chain chainA) throws IOException {
              assertEquals(5000, chainA.readTimeoutMillis());
      
              Chain chainB = chainA.withReadTimeout(100, TimeUnit.MILLISECONDS);
              assertEquals(100, chainB.readTimeoutMillis());
      
              return chainB.proceed(chainA.request());
            }
          };
        }
      

      【讨论】:

      • 你能解释一下这个请求拦截器吗?我正在使用 OKhttp 请求,但它在几秒钟后返回 Timeout 异常。如何将超时设置为更长的时间,以便它可以从服务器获得响应。
      • 你能解释一下这个请求拦截器吗?我正在使用 OKhttp 请求,但它在几秒钟后返回超时异常。如何设置更长的超时时间,以便它可以从服务器获得响应,或者我做错了什么? stackoverflow.com/questions/51443134/…
      • SMH。我不知道为什么不支持Request.Builder().timeout(500)。相反,祝你好运,在查看此请求的代码时注意到隐藏在 DI 中的拦截器。
      【解决方案4】:

      我认为一个好方法是读取拦截器上的自定义标头并将当前超时更改为包含此特定标头的调用,它比创建注释并使用反射来读取它更好。例如:

      class TimeoutInterceptor : Interceptor {
      
          override fun intercept(chain: Interceptor.Chain): Response {
              val request = chain.request()
      
              return request.header("Custom-Timeout")?.let {
                  val newTimeout = it.toInt()
                  chain.withReadTimeout(newTimeout, TimeUnit.MILLISECONDS)
                      .withConnectTimeout(newTimeout, TimeUnit.MILLISECONDS)
                      .withWriteTimeout(newTimeout, TimeUnit.MILLISECONDS)
                      .proceed(request)
              } ?: chain.proceed(request)
          }
      }
      

      【讨论】:

      • 标头将在请求中发送。 +1 注释
      • 在同一个拦截器中设置超时后可以移除阅读器。
      猜你喜欢
      • 1970-01-01
      • 2021-06-24
      • 1970-01-01
      • 2015-10-24
      • 2021-09-29
      • 2014-07-18
      • 1970-01-01
      • 2018-08-28
      • 1970-01-01
      相关资源
      最近更新 更多