分支开始Chain,保存RealCall位置
到了烂大街的责任链了
interceptors先添加okHttpClient对象中的应用层拦截器,紧接着RetryAndFollowUpInterceptor,之所以没在这里初始化,是因为Call对象需要持有RetryAndFollowUpInterceptor对象进行其他操作
接着是顺序添加的拦截器,最后添加了okHttpClient对象中的网络层拦截器
最终形态的Chain,Request经过重重拦截器的包装重整发送到Server,当Response返回时又逆向经历了重重拦截器的解析处理最终到达Client
道理很简单,但OkHttp的实现手法很高明
这里有两个接口Interceptor、Chain
Interceptor即拦截器接口只有一个intercept方法
而Chain只有一个实现类RealInterceptorChain,它是实现责任链的重要类,我更喜欢称其为环
责任链开始之初,实例化了一个环,参数使用的时候再解释
接着环开始滚动proceed,我们以index参数作为环的标记
环不能够滚出链条,所以要判断当前环的位置
当前环滚动(调用)次数自加,判断参数条件,由于当前环各参数为空,所以不throw
紧接着环滚到了下一层,新环index=1,当前index=0的环取出第一个拦截器RetryAndFollowUpInterceptor,处理新环index=1,得到Response
index=i的环内生成index=i+1的新环,并交由拦截器链中第i项拦截器处理,第i项拦截器又调用环的process方法生成新环交给下一个拦截器,一直重复动作,直到环滚到头时,再滚回来,和算法中的递归相似
了解了责任链与环的协作,看看具体的拦截器做了什么
1、 RetryAndFollowUpInterceptor
生成了一个关键对象StreamAllocation,就执行下一个环的滚动。StreamAllocation类是协调Connections、Streams、Calls三者间关系的,使用时讲。
2、BridgeInterceptor
生成新的Requester.builder,在保留原参数的基础上增加新参数,至于具体参数的设置,相信有更加详细准确的资料这里就不显摆了。
我要说下body这个变量,来自RequestBody是个抽象类,有两个实现类,在Request初始化时赋值
RequestBody为网络请求传输的数据载体,共两部分:MediaType、Content,Content根据MediaType的不同而不同,可以是字节、字符串或文件,这三种类型已经满足了所有的请求内容。
可以通过RequestBody中静态方法,实现相应的数据传输
3、CacheInterceptor
用来返回相同请求且结果未更新的Response
分支开始Cache,保存CacheInterceptor位置
缓存来自okHttpClient对象,但在Builder中没有默认实现
需要手动完成,这里有两个Cache:InternalCache、Cache
官方已经不建议使用InternalCache,而是使用Cache替代
但OkHttpClient返回的却还是InternalCache,Cache只是InternalCache的一层具体实现
有点像Cache实现了InternalCache接口的味道,但实际是使用了类聚合
判断是否符合缓存条件,如果是POST、PATCH、PUT、DELETE、MOVE方法则移除缓存,意味着以上方法不缓存。
紧接着判断GET方法,只有GET方法的请求能够被缓存
再来判断vary头信息,具体内容看这里,决定是否缓存
所有条件都通过后,将response转成Entry存入DiskLruCache,key取请求URL的MD5
存取的关键逻辑在Entry类中
通过不同的构造方法,初始化变量
这里使用了okio相关的内容
读写顺序是相反的,按照图中的指示对照着看,如果没接触过okio,单独看会懵
分支结束Cache,读取CacheInterceptor位置
取到候选Response后,交由缓存策略
初始化缓存相关的Http头信息(这里真不是专业的后端开发,未避免误人子弟,各个头信息还请自行查询)
走到这一步,将缓存策略对象strategy交由缓存cache对象,用来统计缓存是否命中
候选Response取到了,但经过缓存策略判断不符合,则关闭候选Response的body,为什么?
cacheCandidate是从Cache中取的Response
经过了Entry的转换
此时snapshot被传入了
由于cacheCandidate从Cache缓存中经okio,取出,如今发现cacheCandidate不符合条件,所以需要关闭okio,类似流的概念
又到了缓存判断的阶段(网络缓存这方面逻辑是真复杂,其实并不是复杂,而是做移动端的应该对此都比较陌生)
经过缓存策略判定根据不同的情况返回请求与response,若请求为空,缓存的response也空,直接返回HttpCode 504(Gateway timeout),为什么?
产生原因在这:
请求不null,但请求的参数设置了onlyIfCached,注释:我有请求参数,但我请求的response必须使用缓存,而不是来自服务器,当缓存不存在时就报了504
终于是最后的判断了:
由策略产出的networkRequest为空得出结论,此请求不需要真正执行,将缓存的response脱光返回
英语讲解:stripper 为strip的名词,双写p加er
走到这一步还没有合适的response,没办法交给下一环处理吧!!
4、ConnectInterceptor
重要的网络连接操作
获取之前环中生成的streamAllocation对象,调用其newStream方法
还记得streamAllocation是在RetryAndFollowUpInterceptor那一环中被实例化赋值的吗?
当时只是实例化对象没做任何操作,具体的参数上面都讲过了,返回看看参数的来源,会对整个流程更加清晰!
这里生成了RealConnection对象,接着RealConnection对象又生成了HttpCodec对象,一个个来
它是Connection接口的唯一实现类,看见红色框是不是很熟悉,BufferedSource、BufferedSink是okio中的输入输出流,有了socket和流就可以进行传输通信了,因此RealConnection是实际的连接类。
看看它是怎么产生的
如方法名,找到一个健康的Connection,何谓健康后面谈,先看看Connection怎么来的
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
boolean foundPooledConnection = false;//是否找到Connection的标记
RealConnection result = null;//找到的Connection
Route selectedRoute = null;//路由
Connection releasedConnection;//需要释放的Connection
Socket toClose;//需要关闭的socket
synchronized (connectionPool) {//锁住connectionPool保证线程安全
if (released) throw new IllegalStateException("released");
if (codec != null) throw new IllegalStateException("codec != null");
if (canceled) throw new IOException("Canceled");
// Attempt to use an already-allocated connection. We need to be careful here because our
// already-allocated connection may have been restricted from creating new streams.
releasedConnection = this.connection;//将自身的Connection记为需要释放的状态
toClose = releaseIfNoNewStreams();//Connection上可以生成多个流,noNewStreams标志表示此Connection是否能够产生新的流
if (this.connection != null) {//复用之前的connection
// We had an already-allocated connection and it's good.
result = this.connection;
releasedConnection = null;
}
if (!reportedAcquired) {//streamAllocation对象中标记是否已经获取到connection
// If the connection was never reported acquired, don't report it as released!
releasedConnection = null;
}
if (result == null) {//走到这里,表明streamAllocation对象中没有可使用的connection
// Attempt to get a connection from the pool.
Internal.instance.get(connectionPool, address, this, null);//从connectionPool池中按要求取一个connection,
if (connection != null) {//取到了
foundPooledConnection = true;
result = connection;
} else {//没取到
selectedRoute = route;
}
}
}
closeQuietly(toClose);
if (releasedConnection != null) {//回调
eventListener.connectionReleased(call, releasedConnection);
}
if (foundPooledConnection) {//回调
eventListener.connectionAcquired(call, result);
}
if (result != null) {
// If we found an already-allocated or pooled connection, we're done.
return result;
}
// If we need a route selection, make one. This is a blocking operation.
boolean newRouteSelection = false;//选择一条连接的路由
if (selectedRoute == null && (routeSelection == null || !routeSelection.hasNext())) {
newRouteSelection = true;
routeSelection = routeSelector.next();
}
synchronized (connectionPool) {//操作池子
if (canceled) throw new IOException("Canceled");
if (newRouteSelection) {//有路由条件
// Now that we have a set of IP addresses, make another attempt at getting a connection from
// the pool. This could match due to connection coalescing.
List<Route> routes = routeSelection.getAll();
for (int i = 0, size = routes.size(); i < size; i++) {
Route route = routes.get(i);
Internal.instance.get(connectionPool, address, this, route);
if (connection != null) {//找到符合路由的connection
foundPooledConnection = true;
result = connection;
this.route = route;
break;
}
}
}
if (!foundPooledConnection) {//没找到
if (selectedRoute == null) {
selectedRoute = routeSelection.next();
}
// Create a connection and assign it to this allocation immediately. This makes it possible
// for an asynchronous cancel() to interrupt the handshake we're about to do.
route = selectedRoute;
refusedStreamCount = 0;
result = new RealConnection(connectionPool, selectedRoute);//根据路由自己创建Connection
acquire(result, false);//通知并赋值
}
}
// If we found a pooled connection on the 2nd time around, we're done.
if (foundPooledConnection) {//如果在池子找到则回调,否则不回调,因为池子找到的connection已经连接好了,否则还要经过下面的连接
eventListener.connectionAcquired(call, result);
return result;
}
// Do TCP + TLS handshakes. This is a blocking operation.
result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
connectionRetryEnabled, call, eventListener);//连接
routeDatabase().connected(result.route());//记录路由
Socket socket = null;
synchronized (connectionPool) {
reportedAcquired = true;
// Pool the connection.
Internal.instance.put(connectionPool, result);//创建的connection放入池子管理
// If another multiplexed connection to the same address was created concurrently, then
// release this connection and acquire that one.
if (result.isMultiplexed()) {//发现池子内部有相同路由的连接(避免不了的多线程操作)舍弃自己,用那个
socket = Internal.instance.deduplicate(connectionPool, address, this);
result = connection;
}
}
closeQuietly(socket);
eventListener.connectionAcquired(call, result);//一切完成回调
return result;
}
经过了重重的筛选,无论是池子中获取的已经连接的connection,还是自行创建并连接放入池子的connection对象,StreamAllocation对象总算持有了connection对象,现在对connection对象进行健康检查
如果此connection对象是新生的不是在池子取的,一定健康
具体的健康检查有:socket是否关闭、http2的话连接是否关闭、非get请求还要检验流是否关闭
如果connection不健康,释放connection关闭socket和流,并从connectionPool中移除
并将与此connection相关联的所有StreamAllocation引用移除
引用是在获取到connection对象时建立的
StreamAllocation对象获取了健康的connection对象,紧接着有用connection对象生成了解码HttpCodec对象
根据http协议的不同生成,Http1时可以看到流在此处配置,那socket和流什么时候初始化的?
前面只说了connection对象从池子取或自己连接,那最初的那个connection对象一定是自连接的,根据这个思路看看RealConnection类
connection对象持有池子connectionPool和路由route对象,连接又是大串的代码
public void connect(int connectTimeout, int readTimeout, int writeTimeout,
int pingIntervalMillis, boolean connectionRetryEnabled, Call call,
EventListener eventListener) {
if (protocol != null) throw new IllegalStateException("already connected");//还没尝试连接就知道协议了,状态肯定错了
RouteException routeException = null;
List<ConnectionSpec> connectionSpecs = route.address().connectionSpecs();
ConnectionSpecSelector connectionSpecSelector = new ConnectionSpecSelector(connectionSpecs);
if (route.address().sslSocketFactory() == null) {//是否https
if (!connectionSpecs.contains(ConnectionSpec.CLEARTEXT)) {//不是https且不支持明文
throw new RouteException(new UnknownServiceException(
"CLEARTEXT communication not enabled for client"));
}
String host = route.address().url().host();
if (!Platform.get().isCleartextTrafficPermitted(host)) {//这里根据okhttp使用平台的不同而判断,是否明文
throw new RouteException(new UnknownServiceException(
"CLEARTEXT communication to " + host + " not permitted by network security policy"));
}
} else {//https的话不允许使用prior_knowledge协议
if (route.address().protocols().contains(Protocol.H2_PRIOR_KNOWLEDGE)) {
throw new RouteException(new UnknownServiceException(
"H2_PRIOR_KNOWLEDGE cannot be used with HTTPS"));
}
}
while (true) {
try {
if (route.requiresTunnel()) {//根据路由判断是建立隧道还是socket连接
connectTunnel(connectTimeout, readTimeout, writeTimeout, call, eventListener);//建立隧道
if (rawSocket == null) {
// We were unable to connect the tunnel but properly closed down our resources.
break;
}
} else {
connectSocket(connectTimeout, readTimeout, call, eventListener);//建立socket
}
establishProtocol(connectionSpecSelector, pingIntervalMillis, call, eventListener);//连接建立起后,初始化协议
eventListener.connectEnd(call, route.socketAddress(), route.proxy(), protocol);//回调连接解释
break;
} catch (IOException e) {//连接异常捕获处理
closeQuietly(socket);
closeQuietly(rawSocket);
socket = null;
rawSocket = null;
source = null;
sink = null;
handshake = null;
protocol = null;
http2Connection = null;
eventListener.connectFailed(call, route.socketAddress(), route.proxy(), null, e);//连接异常回调
if (routeException == null) {
routeException = new RouteException(e);
} else {
routeException.addConnectException(e);
}
if (!connectionRetryEnabled || !connectionSpecSelector.connectionFailed(e)) {//设置重连则不抛
throw routeException;
}
}
}
if (route.requiresTunnel() && rawSocket == null) {//如果路由隧道的代理是http的而请求是https则隧道异常
ProtocolException exception = new ProtocolException("Too many tunnel connections attempted: "
+ MAX_TUNNEL_ATTEMPTS);
throw new RouteException(exception);
}
if (http2Connection != null) {//这里则是http2的多连接复用
synchronized (connectionPool) {
allocationLimit = http2Connection.maxConcurrentStreams();
}
}
}
先看看熟悉的socket连接
如果代理模式为直连或Http则直接生成socket,否则是代理socket
socketFactory从okHttpclient对象传来,默认为DefaultSocketFactory直接new Socket返回
回调连接开始,根据平台连接socket并初始化流。
隧道有中间代理所以要生成代理请求
先创建了socket,后又创建隧道,createTunnel方法创建隧道,方法内先发送隧道请求,紧接着就是阻塞状态下不停的读取数据,直到返回结束状态。
到此connection有了,其内部的socket也已经连接
解码器的功能就是实现socket数据的编解码,操作主要以HttpCodec对象为主,其内部持有了连接好的socket对象
5、CallServerInterceptor
经过前面几环不同的初始化,最后一环终于可以大展拳脚了
//CallServerInterceptor.class
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
HttpCodec httpCodec = realChain.httpStream();
StreamAllocation streamAllocation = realChain.streamAllocation();
RealConnection connection = (RealConnection) realChain.connection();
Request request = realChain.request();
//取出前面几环初始化的对象
long sentRequestMillis = System.currentTimeMillis();//正式请求的时间
realChain.eventListener().requestHeadersStart(realChain.call());//回调
httpCodec.writeRequestHeaders(request);//使用httpCodec发送头信息
realChain.eventListener().requestHeadersEnd(realChain.call(), request);//回调
Response.Builder responseBuilder = null;
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {//方法支持请求体且请求体不空
// If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
// Continue" response before transmitting the request body. If we don't get that, return
// what we did get (such as a 4xx response) without ever transmitting the request body.
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {//请求头中有{"Expect":"100-continue"}表明有请求体数据上传征求服务器意见,具体看服务端有没有实现此协议,若没有client也会在等待超时后发送请求体
httpCodec.flushRequest();//发送头信息
realChain.eventListener().responseHeadersStart(realChain.call());//回调
responseBuilder = httpCodec.readResponseHeaders(true);//读头信息
}
if (responseBuilder == null) {//服务端接受了100-continue
// Write the request body if the "Expect: 100-continue" expectation was met.
realChain.eventListener().requestBodyStart(realChain.call());//回调
long contentLength = request.body().contentLength();//body长度
CountingSink requestBodyOut =
new CountingSink(httpCodec.createRequestBody(request, contentLength));
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);//这两个对象为okio,类似流
request.body().writeTo(bufferedRequestBody);//具体的内容写入流中
bufferedRequestBody.close();//关闭流
realChain.eventListener()
.requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);//回调写出多少数据
} else if (!connection.isMultiplexed()) {//服务端不接受100-continue则设置此connection对象不能生成新流
// 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) {//无body时进入获取response
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(false);
}
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();//在原基础上新建response,这也是构建者模式的优势
int code = response.code();//服务端返回码
if (code == 100) {//服务端返回100表示让client继续发送数据
// server sent a 100-continue even though we did not request one.
// try again to read the actual response
responseBuilder = httpCodec.readResponseHeaders(false);//重新读一遍返回信息
response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
code = response.code();
}
realChain.eventListener()
.responseHeadersEnd(realChain.call(), response);//回调
if (forWebSocket && code == 101) {//请求使用的是webSocket,且服务端已接受
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
response = response.newBuilder()
.body(Util.EMPTY_RESPONSE)//由于websocket的性质,所以给body占位
.build();
} else {//读取返回信息
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {//请求为一次连接or服务端返回关闭连接
streamAllocation.noNewStreams();//使连接不能够产生新流
}
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {//204表示没内容,205表示清空表单,但返回的数据却有长度,明显此次的通信异常了
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
return response;
}
HttpCodec具体的数据读写看实现类Http1Codec,使用的是okio
拼接请求行,抓包时常见
将请求行与头信息写入流
同socket的flush
读取头信息,红框标准的地方也是为什么
这里要判null的原因
请求体的创建包了好几层,chunked表示空请求体,请求体实际也是一个流,FixedLengthSink继承自Sink
finish同flush
创建ResponseBody,此时的body还不是内容,只是流供上层拦截器使用。
最终Response获取到了,环滚到头又要向回滚了
4、ConnectInterceptor
对response没做处理直接返回
3、CacheInterceptor
对返回的response做缓存处理
Response networkResponse = null;
try {
networkResponse = chain.proceed(networkRequest);//上一环返回的response
} finally {
// If we're crashing on I/O or otherwise, don't leak the cache body.
if (networkResponse == null && cacheCandidate != null) {//结果异常,但有缓存,关闭缓存的流
closeQuietly(cacheCandidate.body());
}
}
//为什么只有try-finally,发生异常时不捕获而是向上抛出,在抛出异常前做finally的保护工作。这种写法是为了充分的暴露异常交给外层处理
// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) {//到此处一定有networkResponse,否则早在上步异常了,当有networkResponse且有cacheResponse时根据状态码决定取哪个
if (networkResponse.code() == HTTP_NOT_MODIFIED) {//304表示内容自上次获取后就没被修改过
Response response = cacheResponse.newBuilder()//直接使用缓存cacheResponse
.headers(combine(cacheResponse.headers(), networkResponse.headers()))//将networkResponse和cacheResponse的头信息合并
.sentRequestAtMillis(networkResponse.sentRequestAtMillis())
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
.cacheResponse(stripBody(cacheResponse))//这里只保留cacheResponse除body外的信息,因为body是流
.networkResponse(stripBody(networkResponse))//同上
.build();
networkResponse.body().close();//既然使用缓存,那返回的networkResponse就需要将body流关闭
// 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;//直接返回response
} else {//有缓存但是缓存已过时,不使用cacheResponse,关闭cacheResponse中body的流
closeQuietly(cacheResponse.body());
}
}
Response response = networkResponse.newBuilder()//在返回networkResponse的基础上使用构造者模式
.cacheResponse(stripBody(cacheResponse))//不要body
.networkResponse(stripBody(networkResponse))
.build();
if (cache != null) {//okhttp有缓存策略
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {//返回有body,且http返回信息支持缓存
// Offer this request to the cache.
CacheRequest cacheRequest = cache.put(response);//存入缓存,返回一个带有response.body输入流的cacheRequest
return cacheWritingResponse(cacheRequest, response);//将cacheRequest中的response.body输入流与response的body流连接,新建Response并返回
}
if (HttpMethod.invalidatesCache(networkRequest.method())) {//根据请求方法判断是否缓存,除get外其余不许缓存
try {
cache.remove(networkRequest);
} catch (IOException ignored) {
// The cache cannot be written.
}
}
}
return response;
}
2、BridgeInterceptor
保存cookie和解压
Response networkResponse = chain.proceed(requestBuilder.build());//从上一环接棒
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());//从headers信息中取出cookie,与URL关联后存入cookieJar,CookieJar是一个接口需要手动实现,OkHttp默认没有存储
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);//从这一环传递到下一环的请求信息是经过了加工的,因此返回的时候要将原始的userRequest恢复回来,否则用户会奇怪请求多了些没设置过的参数
if (transparentGzip
&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse)) {//请求是否开启了gzip压缩,默认设置是开了,且Response也使用了gzip压缩,且Response有body
GzipSource responseBody = new GzipSource(networkResponse.body().source());//将body流转为Gzip流
Headers strippedHeaders = networkResponse.headers().newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build();//从headers中移除项
responseBuilder.headers(strippedHeaders);
String contentType = networkResponse.header("Content-Type");
responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));//将原body流切换成Gzip流
}
return responseBuilder.build();
1、RetryAndFollowUpInterceptor
重连
while (true) {//因为重连所以要无限循环
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response;
boolean releaseConnection = true;//释放连接标志
try {
response = realChain.proceed(request, streamAllocation, null, null);//从上环中获取
releaseConnection = false;//是否释放连接,当发生预料外的Exception时释放
} 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.getFirstConnectException();
}
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.
if (priorResponse != null) {//因为有重连,所以要记录上次连接的返回
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
Request followUp;//下步连接操作
try {
followUp = followUpRequest(response, streamAllocation.route());//根据返回状态和路由判断是否有下次连接
} catch (IOException e) {
streamAllocation.release();
throw e;
}
if (followUp == null) {//无下次连接
if (!forWebSocket) {//不是websocket协议
streamAllocation.release();//这次请求完成,关闭连接
}
return response;//返回结果
}
closeQuietly(response.body());//还有下次连接则关闭,response.body的流
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())) {//根据host、port、scheme三个标准判断下次连接与这次是否相同,多次请求与重定向的区别
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;//保存此次的response为priorResponse
}
分支结束Chain,读取RealCall位置
经过了重重拦截器的处理获得了result,来到了最初的地方
完结
这里仅从GET请求出发,就有如此多的内容。要体会OkHttp的深度,不仅仅要具有算法、设计模式、平台特性,这些软件工程师需千锤百炼才能有的技能外,还需要有扎实的网络基础与实际的网络经验。
恰恰网络方面是我的软肋,这次对OkHttp源码的深入,使我对网络通信中关键的点有了模糊的认识。
相信能看到这里的人,自己看一遍call.enqueue应该不是问题。