介绍
拦截器链,采用责任链模式,将一次事物的耦合度降低。
源码分析
RealInterceptorChain
RealInterceptorChain就是个List<Interceptor>,源码比较简单,主要功能proceed
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
...
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec, connection, index + 1,request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
...
return response;
}
RetryAndFollowUpInterceptor
RetryAndFollowUpInterceptor的责任是失败重试和重定向,主要功能在于
intercept
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
//创建一个新的流
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(request.url()), callStackTrace);
int followUpCount = 0;
//重定向可能产生多个Response
Response priorResponse = null;
//循环直到取消或者抛出exception
while (true) {
...
//辅助判断是否要释放连接
boolean releaseConnection = true;
try {
//取得下级返回的response
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (){
//各种异常捕捉处理
...
} finally {
// We're throwing an unchecked exception. Release any resources.
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}
...
//重定向,根据返回response生成新的request
Request followUp = followUpRequest(response);
...
//判断是否是sameConnection(host==host&&port==port&&scheme==scheme)可复用链路
if (!sameConnection(response, followUp.url())) {
streamAllocation.release();
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(followUp.url()), callStackTrace);
} else if (streamAllocation.codec() != null) {
throw new IllegalStateException("Closing the body of " + response
+ " didn't close its backing stream. Bad interceptor?");
}
request = followUp;
priorResponse = response;
}
}
BridgeInterceptor
BridgeInterceptor可以理解成转换器
Bridges from application code to network code. First it builds a network request from a user request. Then it proceeds to call the network. Finally it builds a user response from the network response.
源码非常简单,主要内容在于intercept
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
//组织Request Header包括这是keep-alive, Cookie添加,gzip等
....
//传递
Response networkResponse = chain.proceed(requestBuilder.build());
//组织Response Header 包括cookie保存更新,Gzip解压等
....
return responseBuilder.build();
}
CacheInterceptor
缓存拦截器更具客户端是否支持缓存和相关的缓存策略决定从网络获取或者从缓存获取Response,主要内容在于intercept
public Response intercept(Chain chain) throws IOException {
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
long now = System.currentTimeMillis();
//根据缓存策略获取缓存Request和Response
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
...
//缓存不可用或者缓存过期,网络获取
Response networkResponse = null;
try {
networkResponse = chain.proceed(networkRequest);
} finally {
// If we're crashing on I/O or otherwise, don't leak the cache body.
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}
...
//更新缓存
return response;
}
ConnectInterceptor
ConnectInterceptor建立与服务器的连接
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
//获取可复用流
StreamAllocation streamAllocation = realChain.streamAllocation();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);
//根据HTTP/1.x(keep-alive)和HTTP/2(流复用)的复用机制,发起连接
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
CallServerInterceptor
CallServerInterceptor和服务器交互数据
@Override public Response intercept(Chain chain) throws IOException {
...
long sentRequestMillis = System.currentTimeMillis();
//发送header数据
httpCodec.writeRequestHeaders(request);
Response.Builder responseBuilder = null;
//根据是否支持100-continue,发送body数据
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
...
}
httpCodec.finishRequest();
if (responseBuilder == null) {
responseBuilder = httpCodec.readResponseHeaders(false);
}
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
int code = response.code();
//response处理
...
return response;
}
扩展
责任链中大体流程分析完,其中有很多可以深究的地方,包括缓存和多路复用的实现
缓存
Okhttp缓存涉及到internal cache(接口设计),cache(实现类),CacheStrategy(缓存策略),DiskLruCache(lru cache实现),具体可以参考BlackSwift写的
OkHttp3源码分析[缓存策略] ,OkHttp3源码分析[DiskLruCache]
多路复用
多路复用设计到包括StreamAllocation(上层流),ConnectionPool(连接池), http1Codec,http2Codec。
首先要了解HTTP/1(keep-alive)和HTTP/2(二进制流)的相关知识,才能更好的理解OKhttp多路复用的实现,具体可以参考
OkHttp 3.7源码分析(五)——连接池和
OkHttp3源码分析[复用连接池]