当前位置: 首页 > 知识库问答 >
问题:

未应用来自使用注释的改型拦截器的OkHttp调用超时

钱锐
2023-03-14

我尝试使用OkHttp 3.12.0中最近添加的一个特性:全操作超时。为此,我还依赖retrofit 2.5.0中新的invocation类,它允许我检索方法注释。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Timeout {

    int value();

    TimeUnit unit();

}
public interface AgentApi {

    @Timeout(value = 100, unit = TimeUnit.MILLISECONDS)
    @GET("something")
    Call<String> getSomething();

}
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 Timeout timeout = method != null ? method.getAnnotation(Timeout.class) : null;
        if (timeout != null) {
            chain.call().timeout().timeout(timeout.value(), timeout.unit());
        }
        return chain.proceed(request);
    }

}

尽管它在使用拦截器中的链方法时工作得很好:

chain
  .withConnectTimeout(connect, unit)
  .withReadTimeout(read, unit)
  .withWriteTimeout(write, unit)

这是因为在呼叫排队之前必须设置呼叫超时吗?(而拦截器在过程中触发得太晚了?),或者这是其他原因?

共有1个答案

朱通
2023-03-14

不幸的是你是对的。这是因为OKHttpClient在执行拦截器链之前会获得超时。如果您查看OKHTTP3.RealCall类中的response execute()方法,您将发现timeout.enter()行,这是OKHTTP调度超时的地方,它在getResponseWithInterceptorChain()之前调用,这是执行拦截器的地方。

幸运的是,您可以为此编写解决方案:)将TimeoutInterceptor放入OKHTTP3包中(您可以在应用程序中创建该包)。这将允许您访问具有包可见性的RealCall对象。您的TimeoutInterceptor类应该如下所示:

package okhttp3;

public class TimeoutInterceptor implements Interceptor {

   @Override
   public Response intercept(Chain chain) throws IOException {
       Request request = chain.request();
       Invocation tag = request.tag(Invocation.class);
       Method method = tag != null ? tag.method() : null;
       Timeout timeout = method != null ? method.getAnnotation(Timeout.class) : null;
       if (timeout != null) {
           chain.call().timeout().timeout(timeout.value(), timeout.unit());
           RealCall realCall = (RealCall) chain.call();
           realCall.timeout.enter();
       }
       return chain.proceed(request);
   }
}

解决方法包括在更改超时之后再次执行timeout.enter()。所有的魔法都发生在台词中:

RealCall realCall = (RealCall) chain.call();
realCall.timeout.enter();

祝你好运!

 类似资料:
  • 我以为这些最新版本应该是兼容的。有一条推文;https://twitter.com/JakeWharton/status/553066921675857922和Retrofit 1.9的更新日志也提到了它。 然而,当我尝试这个: 还是不行。setClient方法抱怨不兼容的客户端对象; 我错过了什么?我还看到OkHttpClient没有实现客户端接口。 我现在使用这种方法;https://medi

  • 问题内容: 我创建了一个RestEASY拦截器,以允许我在Web服务调用完成后在HTTP响应上设置标头值。我的代码看起来像这样… 但是,当我调用服务时,永远不会调用拦截器。我看到webservice调用成功完成,但是拦截器中的任何代码都没有执行过。除了注册拦截器,我还需要做些其他事情吗?是否必须在其他任何地方声明?是否需要包含任何特殊的web.xml参数? 问题答案: 您必须在web.xml的re

  • 问题内容: 我想要一个在中运行的应用程序来进行由代码处理的调用。 理想的方法是仅拦截呼叫(简单,只需使用)并“返回”一些数据。 但是,除了使用调用函数外,我没有找到“返回”对的响应的方法。 这对我来说不起作用,因为该应用程序是我无法控制的嵌入式应用程序。就HTML / javascript应用程序而言,它只是进行调用并接收一些数据。 有什么想法吗? 问题答案: 最后,我还是使用了问题中描述的方法。

  • 问题内容: 我正在使用Java EE 6和Jboss AS7.1,并尝试使用拦截器绑定(来自jboss网站的示例)。 我有一个InterceptorBinding注解: 拦截器: 还有一个豆: 但是拦截器没有被称为。。。 在编写此代码时将调用拦截器: 谢谢你的帮助。 问题答案: 您是否按照参考示例中的说明启用了拦截器? 缺省情况下,bean档案没有通过拦截器绑定绑定的已启用拦截器。必须通过将侦听器

  • 我试图在我的应用程序中配置拦截器,但我无法使其工作。 在我的应用程序配置类中,我以以下方式进行了配置: 拦截器: 有人知道为什么没有被调用吗?