问题
In general we set timeout for the okHttp client and we use single instance of that client. So, we can't change the timeout for that client once it's generated.
How to change the timeout for a particular request ?? Is there anyway to do it without creating new client??
It's very common that some calls take more time atleast 1/2 per app, which needs more timeout than others. it would be great if request can override the default timeout.
回答1:
In 3.9 it is possible to set this per request in an interceptor
https://github.com/square/okhttp/blob/36bd68aa3e93affb12504cd40454e64c6812019c/okhttp-tests/src/test/java/okhttp3/InterceptorTest.java#L747-L757
@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());
}
};
}
回答2:
Updating to Retrofit 2.5.0 you can use 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.
In that way, you can create a SpecificTimeout
annotation to be used only on the requests you need to set a different timeout duration and read its values on an OkHttp Interceptor
to change the timeout at that point.
Custom Annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SpecificTimeout {
int duration();
TimeUnit unit();
}
Service Interface
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();
}
Interceptor
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 Builder
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();
回答3:
See https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/PerCallSettings.java which creates shallow clones that only modify some settings.
// Copy to customize OkHttp for this request.
OkHttpClient client2 = client.newBuilder()
.readTimeout(3000, TimeUnit.MILLISECONDS)
.build();
回答4:
I think a good way is to read a custom header on an interceptor and change the current timeout to calls that contains this specific header, it's works better than create a annotation and use reflection to read it. E.G:
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)
}
}
来源:https://stackoverflow.com/questions/46845206/how-to-change-timeout-for-a-request-in-okhttp