一个小胖子从网站做任务的网站故事,域名空间都有了怎么做网站,乌市正规网站建设,wordpress管理文章目录 1. 拦截器1. 拦截器链2. 实际案例1. 注册为应用拦截器2. 注册为网络拦截器 3. 如何选择用哪种拦截器1. 应用拦截器2. 网络层拦截器3. 重写请求4. 重写响应 4. 可用性 2. 事件监听器1. 请求的生命周期2. EventListener使用案例3. EventListener.Factory4. 调用失败的请… 文章目录 1. 拦截器1. 拦截器链2. 实际案例1. 注册为应用拦截器2. 注册为网络拦截器 3. 如何选择用哪种拦截器1. 应用拦截器2. 网络层拦截器3. 重写请求4. 重写响应 4. 可用性 2. 事件监听器1. 请求的生命周期2. EventListener使用案例3. EventListener.Factory4. 调用失败的请求 1. 拦截器
拦截器是一种强大的机制可以用来监测、重写、重试调用。下面是一个简单的例子用来打印请求的输入和输出。
class LoggingInterceptor implements Interceptor {Override public Response intercept(Interceptor.Chain chain) throws IOException {Request request chain.request();long t1 System.nanoTime();logger.info(String.format(Sending request %s on %s%n%s,request.url(), chain.connection(), request.headers()));Response response chain.proceed(request);long t2 System.nanoTime();logger.info(String.format(Received response for %s in %.1fms%n%s,response.request().url(), (t2 - t1) / 1e6d, response.headers()));return response;}
}1. 拦截器链
拦截器可以形成一个拦截器链, 拦截器分为应用层拦截器、网络层拦截器如下图所示:
2. 实际案例
假设我们访问的是http://www.publicobject.com/helloworld.txt它实际上会有一个302跳转到https://publicobject.com/helloworld.txt, OkHttp会自动完成跳转。
我们用上面的LoggingInterceptor做为例子来讲解应用拦截器和网络层拦截器的区别。
1. 注册为应用拦截器
OkHttpClient client new OkHttpClient.Builder().addInterceptor(new LoggingInterceptor()).build();Request request new Request.Builder().url(http://www.publicobject.com/helloworld.txt).header(User-Agent, OkHttp Example).build();Response response client.newCall(request).execute();
response.body().close();输出只会有一个Request一个Response内部的跳转过程没有感知。以下是输出内容:
INFO: Sending request http://www.publicobject.com/helloworld.txt on null
User-Agent: OkHttp ExampleINFO: Received response for https://publicobject.com/helloworld.txt in 1179.7ms
Server: nginx/1.4.6 (Ubuntu)
Content-Type: text/plain
Content-Length: 1759
Connection: keep-alive2. 注册为网络拦截器
OkHttpClient client new OkHttpClient.Builder().addNetworkInterceptor(new LoggingInterceptor()).build();Request request new Request.Builder().url(http://www.publicobject.com/helloworld.txt).header(User-Agent, OkHttp Example).build();Response response client.newCall(request).execute();
response.body().close();输出会感知每一个Request、Response对象。以下为输出内容:
INFO: Sending request http://www.publicobject.com/helloworld.txt on Connection{www.publicobject.com:80, proxyDIRECT hostAddress54.187.32.157 cipherSuitenone protocolhttp/1.1}
User-Agent: OkHttp Example
Host: www.publicobject.com
Connection: Keep-Alive
Accept-Encoding: gzipINFO: Received response for http://www.publicobject.com/helloworld.txt in 115.6ms
Server: nginx/1.4.6 (Ubuntu)
Content-Type: text/html
Content-Length: 193
Connection: keep-alive
Location: https://publicobject.com/helloworld.txtINFO: Sending request https://publicobject.com/helloworld.txt on Connection{publicobject.com:443, proxyDIRECT hostAddress54.187.32.157 cipherSuiteTLS_ECDHE_RSA_WITH_AES_256_CBC_SHA protocolhttp/1.1}
User-Agent: OkHttp Example
Host: publicobject.com
Connection: Keep-Alive
Accept-Encoding: gzipINFO: Received response for https://publicobject.com/helloworld.txt in 80.9ms
Server: nginx/1.4.6 (Ubuntu)
Content-Type: text/plain
Content-Length: 1759
Connection: keep-alive网络层拦截器还能给我们提供更多信息比如Accept-Encoding、Connection这些OkHttp帮我们自动添加的头信息。
3. 如何选择用哪种拦截器
1. 应用拦截器
不关心重定向、重试永远只显示一次即使是从缓存中读取结果只关心应用的原始意图。不关心OkHttp自动添加的头比如If-None-Match等允许短路不调用Chain.process(request)允许重试调用多次Chain.process(request)
2. 网络层拦截器
允许修改和操作中间的请求结果和状态从缓存中读取时不调用拦截器关心中间过程访问请求的Connection对象
3. 重写请求
拦截器可以添加、删除、修改HTTP头甚至可以改变请求体。比如你可以在拦截器内对请求体做压缩(如果你知道Web服务器支持的话)。
final class GzipRequestInterceptor implements Interceptor {Override public Response intercept(Interceptor.Chain chain) throws IOException {Request originalRequest chain.request();if (originalRequest.body() null || originalRequest.header(Content-Encoding) ! null) {return chain.proceed(originalRequest);}Request compressedRequest originalRequest.newBuilder().header(Content-Encoding, gzip).method(originalRequest.method(), gzip(originalRequest.body())).build();return chain.proceed(compressedRequest);}private RequestBody gzip(final RequestBody body) {return new RequestBody() {Override public MediaType contentType() {return body.contentType();}Override public long contentLength() {return -1; // We dont know the compressed length in advance!}Override public void writeTo(BufferedSink sink) throws IOException {BufferedSink gzipSink Okio.buffer(new GzipSink(sink));body.writeTo(gzipSink);gzipSink.close();}};}
}4. 重写响应
重写响应可以重写响应的HTTP头、响应内容等一般来说是不推荐的可能会违反直觉。
比如为响应自动添加缓存头。
private static final Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR new Interceptor() {Override public Response intercept(Interceptor.Chain chain) throws IOException {Response originalResponse chain.proceed(chain.request());return originalResponse.newBuilder().header(Cache-Control, max-age60).build();}
};4. 可用性
需要okhttp 2.2以后的版本不可以和OkUrlFactory一起工作不能在Retrofit 1.8及以下版本使用。
2. 事件监听器
你可以通过事件知道OkHttp的内部运行状态。通过时间我们可以监控:
HTTP请求的大小、频率这些请求对应的网络性能
(这里提及的API还不是最终版的APIOkHttp 3.9里这个API只是非稳定的预览版预计在3.10、3.11会稳定)
你可以通过继承EventListener并覆盖你感兴趣的事件方法来获取通知。
1. 请求的生命周期
一次普通的请求完成会触发以下事件:
2. EventListener使用案例
class PrintingEventListener extends EventListener {private long callStartNanos;private void printEvent(String name) {long nowNanos System.nanoTime();if (name.equals(callStart)) {callStartNanos nowNanos;}long elapsedNanos nowNanos - callStartNanos;System.out.printf(%.3f %s%n, elapsedNanos / 1000000000d, name);}Override public void callStart(Call call) {printEvent(callStart);}Override public void callEnd(Call call) {printEvent(callEnd);}Override public void dnsStart(Call call, String domainName) {printEvent(dnsStart);}Override public void dnsEnd(Call call, String domainName, ListInetAddress inetAddressList) {printEvent(dnsEnd);}...
}EventListener会被所有Call共享所以EventListener本身不能是有状态的如果需要的话采用EventListener.Factory
3. EventListener.Factory
Factory可以让相同的Call共用一个EventListener对象也可以只是随机选一部分Call来监听
class MetricsEventListener extends EventListener {private static final Factory FACTORY new Factory() {Override public EventListener create(Call call) {if (Math.random() 0.10) {return new MetricsEventListener(call);} else {return EventListener.NONE;}}};...
}4. 调用失败的请求
如果是连接阶段触发事件connectFailed()否则触发callFailed()。失败发生的时候有可能存在调用了start方法但是没有调用end方法的情况。