上一篇文章我们知道okhttp异步真正执行的实际是RealCall这个类的 getResponseWithInterceptorChain 方法
Response getResponseWithInterceptorChain() throws IOException {
// 拦截器的一个集合
List<Interceptor> interceptors = new ArrayList<>();
// 客户端的所有自定义拦截器
interceptors.addAll(client.interceptors());// 自己的定义的拦截器
// OKhttp 5 个拦截器 ,责任链设计模式,每一个拦截器只处理与他相关的部分 volley
interceptors.add(retryAndFollowUpInterceptor);// 重试
interceptors.add(new BridgeInterceptor(client.cookieJar()));// 基础
interceptors.add(new CacheInterceptor(client.internalCache()));// 缓存
interceptors.add(new ConnectInterceptor(client));// 建立连接
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);
}
RetryAndFollowUpInterceptor拦截器
直接看intercept的源码
@Override public Response intercept(Chain chain) throws IOException {
//获得上一个的request
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Call call = realChain.call();
EventListener eventListener = realChain.eventListener();
streamAllocation = new StreamAllocation(client.connectionPool(), createAddress(request.url()),
call, eventListener, callStackTrace);
int followUpCount = 0;
Response priorResponse = null;
while (true) {
// 一个死循环 ,重试, 两种情况可以终止 return Response, throws IOException
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response;
boolean releaseConnection = true;
try {
// 丢给下一个拦截器去处理,会有异常的情况
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.
// 先处理 RouteException
if (!recover(e.getLastConnectException(), false, request)) {
throw e.getLastConnectException();
}
releaseConnection = false;
continue;
} catch (IOException e) {
// 处理 IOException
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, requestSendStarted, request)) throw e;
releaseConnection = false;
continue;
} finally {
// We're throwing an unchecked exception. Release any resources.
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}
//默认priorResponse为空
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
// 从下面过了一个 Response ,但是这个 Response 不能够直接返回给上一级,会有重定向
// 重定向 返回码是 307 ,从头部的 Location 里面获取新的链接 重新请求一次
Request followUp = followUpRequest(response);
if (followUp == null) {
if (!forWebSocket) {
streamAllocation.release();
}
// 直接返回给上一级
return response;
}
// 请求就变为了重试的请求 Request
request = followUp;
priorResponse = response;
}
}
followUpRequest源码分析
switch (responseCode) {
case HTTP_TEMP_REDIRECT://307
//如果不是get方法且不是head方法直接返回一个空
if (!method.equals("GET") && !method.equals("HEAD")) {
return null;
}
case HTTP_SEE_OTHER:
if (!client.followRedirects()) return null;
// 从头部信息里面 获取 Location 新的链接
String location = userResponse.header("Location");
if (location == null) return null;
// 生成一个新的 HttpUrl
HttpUrl url = userResponse.request().url().resolve(location);
// 返回一个新链接的 Request
return requestBuilder.url(url).build();
}
**处理重试的一个拦截器,会去处理一些异常,只要不是致命的异常就会重新发起一次请求(把Request给下级),如果是致命的异常就会抛给上一级;
会处理一些重定向等等,比如 3XX 307、407 就会从头部中获取新的路径,生成一个新的请求交给下一级(重新发起一次请求)**
BridgeInterceptor桥接拦截器:编码,压缩等添加头部作用
@Override public Response intercept(Chain chain) throws IOException {
//获得上一次的请求
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
//获得请求参数
RequestBody body = userRequest.body();
if (body != null) {
MediaType contentType = body.contentType();
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString());
}
//长度
long contentLength = body.contentLength();
if (contentLength != -1) {
requestBuilder.header("Content-Length", Long.toString(contentLength));
requestBuilder.removeHeader("Transfer-Encoding");
} else {
requestBuilder.header("Transfer-Encoding", "chunked");
requestBuilder.removeHeader("Content-Length");
}
}
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", hostHeader(userRequest.url(), false));
}
// 这个头信息必须要,但是使用者不一定会自己设置
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive");
}
// If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
boolean transparentGzip = false;
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true;
requestBuilder.header("Accept-Encoding", "gzip");
}
//保存cookie信息
List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));
}
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", Version.userAgent());
}
// 下一级给我们的 Response
Response networkResponse = chain.proceed(requestBuilder.build());
// 处理保存 Cookie
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
// 如果后台返回的数据采用 gzip 压缩了
if (transparentGzip
&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse)) {
// GzipSource okio 的输入流
GzipSource responseBody = new GzipSource(networkResponse.body().source());
Headers strippedHeaders = networkResponse.headers().newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build();
responseBuilder.headers(strippedHeaders);
String contentType = networkResponse.header("Content-Type");
responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
}
// 我们这个拦截器处理的 Response 返回回去
return responseBuilder.build();
}
**做一个简单的处理,设置一些通用的请求头,Content-Type Connection Content-Length Cookie
做一些返回的处理,如果返回的数据被压缩了采用 ZipSource , 保存 Cookie**
CacheInterceptor缓存拦截器
@Override public Response intercept(Chain chain) throws IOException {
// 从缓存里面拿
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
long now = System.currentTimeMillis();
// 构建了一个缓存策略 ①,networkRequest 不为空,且设置只从缓冲中获取,两者此时都设置为空
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
if (cache != null) {
cache.trackResponse(strategy);
}
if (cacheCandidate != null && cacheResponse == null) {
closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
}
// If we're forbidden from using the network and the cache is insufficient, fail.
// 如果请求的 networkRequest 是空,缓存的 cacheResponse 是空 就返回 504错误
// 指定该数据只从缓存获取的时候两个都为空
if (networkRequest == null && cacheResponse == null) {
return new Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(504)
.message("Unsatisfiable Request (only-if-cached)")
.body(Util.EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
}
// If we don't need the network, we're done.
if (networkRequest == null) {
// 如果缓存策略里面的 networkRequest 是空,那么就 返回 缓存好的 Response
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
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());
}
}
// 处理返回,处理 304 的情况
if (cacheResponse != null) {
if (networkResponse.code() == HTTP_NOT_MODIFIED) {
// 服务器数据没有变化,你还是可以拿之前的缓存
Response response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers(), networkResponse.headers()))
.sentRequestAtMillis(networkResponse.sentRequestAtMillis())
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
networkResponse.body().close();
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
cache.trackConditionalCacheHit();
cache.update(cacheResponse, response);
return response;
} else {
closeQuietly(cacheResponse.body());
}
}
// 要返回 response
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
if (cache != null) {
// 然后缓存获取的 Response
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
// Offer this request to the cache.
CacheRequest cacheRequest = cache.put(response);
return cacheWritingResponse(cacheRequest, response);
}
if (HttpMethod.invalidatesCache(networkRequest.method())) {
try {
cache.remove(networkRequest);
} catch (IOException ignored) {
// The cache cannot be written.
}
}
}
return response;
}
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();①进行分析
public Factory(long nowMillis, Request request, Response cacheResponse) {
this.nowMillis = nowMillis;
this.request = request;
this.cacheResponse = cacheResponse;
//如果缓存不等于空
if (cacheResponse != null) {
this.sentRequestMillis = cacheResponse.sentRequestAtMillis();
this.receivedResponseMillis = cacheResponse.receivedResponseAtMillis();
//获得缓存的头部
Headers headers = cacheResponse.headers();
//头部信息for循环
for (int i = 0, size = headers.size(); i < size; i++) {
String fieldName = headers.name(i);
String value = headers.value(i);
// 解析之前缓存好的一些头部信息,服务器给的 Expires 缓存的过期时间,Last-Modified 服务器上次数据的更新时间
if ("Date".equalsIgnoreCase(fieldName)) {
servedDate = HttpDate.parse(value);
servedDateString = value;
} else if ("Expires".equalsIgnoreCase(fieldName)) {
expires = HttpDate.parse(value);
} else if ("Last-Modified".equalsIgnoreCase(fieldName)) {
lastModified = HttpDate.parse(value);
lastModifiedString = value;
} else if ("ETag".equalsIgnoreCase(fieldName)) {
etag = value;
} else if ("Age".equalsIgnoreCase(fieldName)) {
ageSeconds = HttpHeaders.parseSeconds(value, -1);
}
}
}
}
//get源码
public CacheStrategy get() {
//判断是不是要缓存
CacheStrategy candidate = getCandidate();
// onlyIfCached 只能从缓存里面获取
// networkRequest = null cacheResponse = null
if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) {
// 这时候netwrodRequest和cacheResponse都为空.
return new CacheStrategy(null, null);
}
return candidate;
}
在缓存可用的情况下,读取本地的缓存的数据,如果没有直接去服务器,如果有首先判断有没有缓存策略,然后判断有没有过期,如果没有过期直接拿缓存,如果过期了需要添加一些之前头部信息如:If-Modified-Since ,这个时候后台有可能会给你返回 304 代表你还是可以拿本地缓存,每次读取到新的响应后做一次缓存。
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, chain, doExtensiveHealthChecks);
// 拿到一个连接
RealConnection connection = streamAllocation.connection();
// 把这个连接传给下一级
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
* HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);*
try {
// 找一个连接,首先判断有没有健康的,没有就创建(建立Scoket,握手连接),连接缓存
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);
// 封装 HttpCodec 里面封装了 okio 的 Source(输入) 和 Sink (输出) ②
HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);
synchronized (connectionPool) {
codec = resultCodec;
return resultCodec;
}
} catch (IOException e) {
throw new RouteException(e);
}
findHealthyConnection源码分析我们可以知道主要的方法就是findConnection
RealConnection result = null;
if (result == null) {
//如果为空,从缓存池中获得连接
Internal.instance.get(connectionPool, address, this, null);
if (connection != null) {
foundPooledConnection = true;
result = connection;
} else {
selectedRoute = route;
}
}
}
// 没有找到可用的连接,自己new一个
result = new RealConnection(connectionPool, selectedRoute);
acquire(result, false);
// 真正建立连接 RealConnection -> connect
result.connect(
connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled, call, eventListener);
routeDatabase().connected(result.route());
result.connect这里面主要的代码 connectSocket(connectTimeout, readTimeout, call, eventListener);
try {
//看这个
Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
} catch (ConnectException e) {
ConnectException ce = new ConnectException("Failed to connect to " + route.socketAddress());
ce.initCause(e);
throw ce;
}
public void connectSocket(Socket socket, InetSocketAddress address,
int connectTimeout) throws IOException {
//由此我们可以知道,okhttp是根据socket进行连接的
socket.connect(address, connectTimeout);
}
看下②的 HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);
public HttpCodec newCodec(OkHttpClient client, Interceptor.Chain chain,
StreamAllocation streamAllocation) throws SocketException {
if (http2Connection != null) {
return new Http2Codec(client, chain, streamAllocation, http2Connection);
} else {
socket.setSoTimeout(chain.readTimeoutMillis());
// Okio : 基于原生的IO的封装 ,IO涉及类和方法忒多,source(输入) sink(输出)
source.timeout().timeout(chain.readTimeoutMillis(), MILLISECONDS);
sink.timeout().timeout(chain.writeTimeoutMillis(), MILLISECONDS);
// Http1Codec 就是 封装了 source 和 sink 就是自己的输入输出流,本质就是操作 Socket 的输入输出流
return new Http1Codec(client, streamAllocation, source, sink);
}
}
findHealthyConnection() 找一个连接,首先判断有没有健康的,没有就创建(建立Scoket,握手连接),连接缓存得到一条结论:OkHttp 是基于原生的 Socket + okio(原生IO的封装)
封装 HttpCodec 里面封装了 okio 的 Source(输入) 和 Sink (输出),我们通过 HttpCodec 就可以操作 Socket的输入输出,我们就可以像服务器写数据和读取返回数据
CallServerInterceptor写数据拦截器
@Override public Response intercept(Chain chain) throws IOException {
realChain.eventListener().requestHeadersStart(realChain.call());
// 写一些请求头的数据
httpCodec.writeRequestHeaders(request);
realChain.eventListener().requestHeadersEnd(realChain.call(), request);
Response.Builder responseBuilder = null;
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
httpCodec.flushRequest();
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(true);
}
if (responseBuilder == null) {
// Write the request body if the "Expect: 100-continue" expectation was met.
realChain.eventListener().requestBodyStart(realChain.call());
long contentLength = request.body().contentLength();
CountingSink requestBodyOut =
new CountingSink(httpCodec.createRequestBody(request, contentLength));
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
// 写数据,表单数据,文件
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
realChain.eventListener()
.requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
} else if (!connection.isMultiplexed()) {
// If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
// from being reused. Otherwise we're still obligated to transmit the request body to
// leave the connection in a consistent state.
streamAllocation.noNewStreams();
}
}
httpCodec.finishRequest();
if (responseBuilder == null) {
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(false);
}
// 返回一个 Response
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
realChain.eventListener()
.responseHeadersEnd(realChain.call(), response);
int code = response.code();
if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
response = response.newBuilder()
.body(Util.EMPTY_RESPONSE)
.build();
} else {
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();
}
//204代表成功访问服务器但是没有内容返回
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
return response;
}
写数据和读取数据
写头部信息,写body表单信息等等
连接三个核心类(连接复用)
RealConnection、ConnectionPool、StreamAllocation
RealConnection: 建立连接的一个对象的封装
ConnectionPool:保存了连接
StreamAllocation: 找一些连接,做一下封装