OkHttp Время ожидания вызова от Retrofit Interceptor с использованием аннотаций не применяется
Я пытаюсь использовать недавно добавленную функцию из OkHttp 3.12.0: тайм-ауты для полной операции. Для этого я также полагаюсь на новый Invocation
класс из модернизации 2.5.0, который позволяет мне получать аннотации метода.
Аннотация:
@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);
}
}
Я правильно добавил TimeoutInterceptor с .addInterceptor(...)
в OkHttpClient, предоставленном для Retrofit Builder.
К сожалению, это не работает, как я ожидал. Звонки не прерываются после истечения времени ожидания?
Хотя он прекрасно работает при использовании цепочечных методов от перехватчика:
chain
.withConnectTimeout(connect, unit)
.withReadTimeout(read, unit)
.withWriteTimeout(write, unit)
Это связано с тем, что время ожидания вызова должно быть установлено до постановки в очередь? (и перехватчик срабатывает слишком поздно в процессе?), или это что-то еще?
0 ответов
К сожалению, вы правы. Это потому что OkHttpClient
получает таймауты до того, как запустит цепочку перехватчиков. Если вы посмотрите на Response execute()
метод в классе okhttp3.RealCall вы найдете строку 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();
Удачи!