系统学习详见OKhttp源码解析详解系列
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
从上述代码可以看出拦截器调用的先后顺序依次是
client.interceptors()-->RetryAndFollowUpInterceptor-->BridgeInterceptor-->CacheInterceptor-->ConnectInterceptor-->client.networkInterceptors()-->CallServerInterceptor
1 Interceptor接口
所有的拦截器(包括我们自定义的)都实现了Interceptor接口
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
/**
* 返回请求将在其上执行的连接。
* 这只适用于网络拦截器的chains; 对于应用程序拦截器,这总是空的。
*/
@Nullable
Connection connection();
Call call();
int connectTimeoutMillis();
Chain withConnectTimeout(int timeout, TimeUnit unit);
int readTimeoutMillis();
Chain withReadTimeout(int timeout, TimeUnit unit);
int writeTimeoutMillis();
Chain withWriteTimeout(int timeout, TimeUnit unit);
}
2 Okhttp内置的拦截器
- RetryAndFollowUpInterceptor:负责失败重试以及重定向。
- BridgeInterceptor:负责把用户构造的请求转换为发送给服务器的请求,把服务器返回的响应转换为对用户友好的响应。
- CacheInterceptor:负责读取缓存以及更新缓存。
- ConnectInterceptor:负责与服务器建立连接。
- CallServerInterceptor:负责从服务器读取响应的数据。
2.1 RetryAndFollowUpInterceptor
RetryAndFollowUpInterceptor负责失败重试以及重定向。
RetryAndFollowUpInterceptor的作用就是处理了一些连接异常以及重定向。
- 整个方法的流程
- 构建一个StreamAllocation对象,StreamAllocation相当于是个管理类,维护了Connections、Streams和Calls之间的管理,该类初始化一个Socket连接对象,获取输入/输出流对象。
- 继续执行下一个Interceptor,即BridgeInterceptor
- 抛出异常,则检测连接是否还可以继续,以下情况不会重试:
客户端配置出错不再重试
出错后,request body不能再次发送
发生以下Exception也无法恢复连接
ProtocolException:协议异常
InterruptedIOException:中断异常
SSLHandshakeException:SSL握手异常
SSLPeerUnverifiedException:SSL握手未授权异常
没有更多线路可以选择。- 根据响应码处理请求,返回Request不为空时则进行重定向处理,重定向的次数不能超过20次。
- 由followUpRequest()方法完成
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Call call = realChain.call();
EventListener eventListener = realChain.eventListener();
//1. 构建一个StreamAllocation对象,StreamAllocation相当于是个管理类,维护了
//Connections、Streams和Calls之间的管理,该类初始化一个Socket连接对象,获取输入/输出流对象。
StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(request.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
//重定向次数
int followUpCount = 0;
Response priorResponse = null;
while (true) {
if (canceled) {
//删除连接上的call请求
streamAllocation.release();
throw new IOException("Canceled");
}
Response response;
boolean releaseConnection = true;
try {
//2. 继续执行下一个Interceptor,即BridgeInterceptor
response = realChain.proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (RouteException e) {
//抛出异常以指示通过单个路由连接的问题。
// The attempt to connect via a route failed. The request will not have been sent.
//尝试通过路由进行连接失败。 该请求不会被发送
if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
throw e.getLastConnectException();
}
releaseConnection = false;
continue;
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
//尝试与服务器通信失败。 该请求可能已发送
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, streamAllocation, requestSendStarted, request)) throw e;
releaseConnection = false;
continue;
} finally {
// We're throwing an unchecked exception. Release any resources.
// 检测到其他未知异常,则释放连接和资源
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}
// Attach the prior response if it exists. Such responses never have a body.
//构建响应体,这个响应体的body为空。
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
Request followUp;
try {
// 根据响应码处理请求,返回Request不为空时则进行重定向处理
followUp = followUpRequest(response, streamAllocation.route());
} catch (IOException e) {
streamAllocation.release();
throw e;
}
if (followUp == null) {
if (!forWebSocket) {
streamAllocation.release();
}
return response;
}
closeQuietly(response.body());
//重定向的次数不能超过20次
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
if (followUp.body() instanceof UnrepeatableRequestBody) {
streamAllocation.release();
throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
}
if (!sameConnection(response, followUp.url())) {
streamAllocation.release();
streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(followUp.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
} 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;
}
}
- StreamAllocation这个类的作用
- 这个类协调了三个实体类的关系:
- Connections:连接到远程服务器的物理套接字,这个套接字连接可能比较慢,所以它有一套取消机制。
- Streams:定义了逻辑上的HTTP请求/响应对,每个连接都定义了它们可以携带的最大并发流,HTTP/1.x每次只可以携带一个,HTTP/2每次可以携带多个。
- Calls:定义了流的逻辑序列,这个序列通常是一个初始请求以及它的重定向请求,对于同一个连接,我们通常将所有流都放在一个调用中,以此来统一它们的行为。
followUpRequest()