如何在 okhttp 中更改请求的超时时间
How to change timeout for a request in okhttp
通常我们为 okHttp 客户端设置超时,并且我们使用该客户端的单个实例。因此,一旦客户端生成,我们就无法更改该客户端的超时时间。
如何更改特定请求的超时时间??到底有没有
在不创建新客户的情况下做到这一点??
很常见的是,某些调用花费的时间至少是每个应用程序的 1/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.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());
}
};
}
更新到 Retrofit 2.5.0 您可以使用 Invocation
class
New: Invocation class provides a reference to the invoked method and argument list as a tag on the underlying OkHttp Call. This can be accessed from an OkHttp interceptor for things like logging, analytics, or metrics aggregation.
这样,您可以创建一个 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();
我认为一个好方法是在拦截器上读取自定义 header 并将当前超时更改为包含此特定 header 的调用,它比创建注释和使用反射效果更好阅读它。例如:
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)
}
}
通常我们为 okHttp 客户端设置超时,并且我们使用该客户端的单个实例。因此,一旦客户端生成,我们就无法更改该客户端的超时时间。
如何更改特定请求的超时时间??到底有没有 在不创建新客户的情况下做到这一点??
很常见的是,某些调用花费的时间至少是每个应用程序的 1/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.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());
}
};
}
更新到 Retrofit 2.5.0 您可以使用 Invocation
class
New: Invocation class provides a reference to the invoked method and argument list as a tag on the underlying OkHttp Call. This can be accessed from an OkHttp interceptor for things like logging, analytics, or metrics aggregation.
这样,您可以创建一个 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();
我认为一个好方法是在拦截器上读取自定义 header 并将当前超时更改为包含此特定 header 的调用,它比创建注释和使用反射效果更好阅读它。例如:
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)
}
}