【发布时间】:2021-02-10 12:41:33
【问题描述】:
一般来说,我们为 okHttp 客户端设置超时,并且我们使用该客户端的单个实例。因此,一旦生成该客户端,我们就无法更改它的超时时间。
如何更改特定请求的超时时间?到底有没有 在不创建新客户的情况下做到这一点?
某些调用需要更多时间(每个应用至少 1/2)是很常见的,这比其他调用需要更多的超时时间。如果请求可以覆盖默认超时,那就太好了。
【问题讨论】:
一般来说,我们为 okHttp 客户端设置超时,并且我们使用该客户端的单个实例。因此,一旦生成该客户端,我们就无法更改它的超时时间。
如何更改特定请求的超时时间?到底有没有 在不创建新客户的情况下做到这一点?
某些调用需要更多时间(每个应用至少 1/2)是很常见的,这比其他调用需要更多的超时时间。如果请求可以覆盖默认超时,那就太好了。
【问题讨论】:
更新到 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();
【讨论】:
@Retention 和 @Target 注释更改为:@Retention(AnnotationRetention.RUNTIME) 和 @Target(AnnotationTarget.FUNCTION)。
参见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.9 中,可以在拦截器中为每个请求设置此项
@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 异常。如何将超时设置为更长的时间,以便它可以从服务器获得响应。
Request.Builder().timeout(500)。相反,祝你好运,在查看此请求的代码时注意到隐藏在 DI 中的拦截器。
我认为一个好方法是读取拦截器上的自定义标头并将当前超时更改为包含此特定标头的调用,它比创建注释并使用反射来读取它更好。例如:
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)
}
}
【讨论】: