使用注释的改造拦截器的 OkHttp 调用超时未应用
OkHttp Call timeout from Retrofit Interceptor using Annotations is not applied
我正在尝试使用 OkHttp 3.12.0 最近添加的功能:全操作超时。
为此,我还依赖于改造 2.5.0 中的新 Invocation
class,它允许我检索方法注释。
注释为:
@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);
}
}
我已经在提供给 Retrofit Builder 的 OkHttpClient 中使用 .addInterceptor(...)
正确添加了 TimeoutInterceptor。
不幸的是,它没有像我预期的那样工作。达到超时时调用不会失败?
尽管使用拦截器中的链方法时它工作正常:
chain
.withConnectTimeout(connect, unit)
.withReadTimeout(read, unit)
.withWriteTimeout(write, unit)
这是因为必须在呼叫入队之前设置呼叫超时吗? (并且拦截器在此过程中触发得太晚了?),还是其他原因?
不幸的是你是对的。这是因为 OkHttpClient
在执行拦截器链之前超时。如果您查看 okhttp3.RealCall class 中的 Response execute()
方法,您会发现第 timeout.enter()
行是 OkHttp
安排超时的地方,它在 getResponseWithInterceptorChain()
之前被调用这是执行拦截器的地方。
幸运的是,您可以为此编写解决方法:)
将您的 TimeoutInterceptor
放入 okhttp3
包中(您可以在您的应用中创建该包)。这将允许您访问具有包可见性的 RealCall
对象。您的 TimeoutInterceptor
class 应如下所示:
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();
祝你好运!
我正在尝试使用 OkHttp 3.12.0 最近添加的功能:全操作超时。
为此,我还依赖于改造 2.5.0 中的新 Invocation
class,它允许我检索方法注释。
注释为:
@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);
}
}
我已经在提供给 Retrofit Builder 的 OkHttpClient 中使用 .addInterceptor(...)
正确添加了 TimeoutInterceptor。
不幸的是,它没有像我预期的那样工作。达到超时时调用不会失败?
尽管使用拦截器中的链方法时它工作正常:
chain
.withConnectTimeout(connect, unit)
.withReadTimeout(read, unit)
.withWriteTimeout(write, unit)
这是因为必须在呼叫入队之前设置呼叫超时吗? (并且拦截器在此过程中触发得太晚了?),还是其他原因?
不幸的是你是对的。这是因为 OkHttpClient
在执行拦截器链之前超时。如果您查看 okhttp3.RealCall class 中的 Response execute()
方法,您会发现第 timeout.enter()
行是 OkHttp
安排超时的地方,它在 getResponseWithInterceptorChain()
之前被调用这是执行拦截器的地方。
幸运的是,您可以为此编写解决方法:)
将您的 TimeoutInterceptor
放入 okhttp3
包中(您可以在您的应用中创建该包)。这将允许您访问具有包可见性的 RealCall
对象。您的 TimeoutInterceptor
class 应如下所示:
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();
祝你好运!