Okhttp解析—Interceptor详解

Interceptor可以说是okhttp的精髓之一,Okhttp重写请求/响应、重试、缓存响应等操作,基本都是在各个Interceptor中完成的,上篇文章分析Okhttp运行流程时只是简单带过,那么这篇文章就来详细分析下Interceptor以及拦截器链机制。

一、Interceptor以及InterceptorChain

/*** Observes, modifies, and potentially short-circuits requests going out and the corresponding* responses coming back in. Typically interceptors add, remove, or transform headers on the request* or response.*/
public interface Interceptor {Response intercept(Chain chain) throws IOException;interface Chain {Request request();Response proceed(Request request) throws IOException;/*** Returns the connection the request will be executed on. This is only available in the chains* of network interceptors; for application interceptors this is always null.*/@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);}
}

Interceptor最主要的就是其intercept()函数,InterceptorChain则是Interceptor一个内部类,它的主要作用就是链式有序调用Interceptor。

Okhttp内置了5种Interceptor它们分别是RetryAndFollowUpInterceptor
、BridgeInterceptor、CacheInterceptor、ConnectInterceptor
、CallServerInterceptor。正是这5种Interceptor完成了重写、重试、缓存、请求等操作,接下来我们来逐一分析它们的作用。

二、Interceptor链式调用

拦截器调用的入口是在ReallCall的getResponseWithInterceptorChain()函数中。

Response getResponseWithInterceptorChain() throws IOException {// Build a full stack of interceptors.List<Interceptor> interceptors = new ArrayList<>();interceptors.addAll(client.interceptors());interceptors.add(new RetryAndFollowUpInterceptor(client));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, transmitter, null, 0,originalRequest, this, client.connectTimeoutMillis(),client.readTimeoutMillis(), client.writeTimeoutMillis());//1boolean calledNoMoreExchanges = false;try {Response response = chain.proceed(originalRequest);//2if (transmitter.isCanceled()) {closeQuietly(response);throw new IOException("Canceled");}return response;} catch (IOException e) {calledNoMoreExchanges = true;throw transmitter.noMoreExchanges(e);} finally {if (!calledNoMoreExchanges) {transmitter.noMoreExchanges(null);}}}

我们看到该函数一开始就构造了一个List然后向里边添加所有的Interceptor,包括我们自定义的Application Interceptor、系统预置的Interceptor、自定义的Network Interceptor等。
然后在注释1处构造RealInterceptorChain实例并传入刚刚的interceptor list还有就是我们看到第四个参数此处传入的是0。该参数表示接下来要调用interceptor list中哪个interceptor。
最后在注释2处调用chain.proceed()并传入原始请求。

@Override public Response proceed(Request request) throws IOException {return proceed(request, transmitter, exchange);
}public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)throws IOException {if (index >= interceptors.size()) throw new AssertionError();calls++;// If we already have a stream, confirm that the incoming request will use it.if (this.exchange != null && !this.exchange.connection().supportsUrl(request.url())) {throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)+ " must retain the same host and port");}// If we already have a stream, confirm that this is the only call to chain.proceed().if (this.exchange != null && calls > 1) {throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)+ " must call proceed() exactly once");}// Call the next interceptor in the chain.RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,index + 1, request, call, connectTimeout, readTimeout, writeTimeout);//1Interceptor interceptor = interceptors.get(index);//2Response response = interceptor.intercept(next);//3// Confirm that the next interceptor made its required call to chain.proceed().if (exchange != null && index + 1 < interceptors.size() && next.calls != 1) {throw new IllegalStateException("network interceptor " + interceptor+ " must call proceed() exactly once");}// Confirm that the intercepted response isn't null.if (response == null) {throw new NullPointerException("interceptor " + interceptor + " returned null");}if (response.body() == null) {throw new IllegalStateException("interceptor " + interceptor + " returned a response with no body");}return response;
}

proceed函数我们主要关注3个注释处的操作。
在注释1处构造RealInterceptorChain,其中参数4此时变为index+1,index就是之前在getResponseWithInterceptorChain中构造RealInterceptorChain时传入的值。

注释2处通过index从Interceptor list中取出对应的Interceptor。
注释3处调用Interceptor的intercept()方法,参数传入注释1处构造的RealInterceptorChain。因为Interceptor的操作都是在intercept()函数中完成的,所以该操作完成了当前Interceptor的调用。同时在每个Interceptor的intercept()函数中都会调用next.proceed()这样就开启了下一个Interceptor调用,如此反复最终像链条一样依次调用Interceptor list中所有的Interceptor。

三、详解各个Interceptor

上边我们分析了Interceptor是按顺序调用的,这里的顺序指的是添加到Interceptor list中的先后。由getResponseWithInterceptorChain()方法可以调用的顺序依次是(未考虑添加自定义Interceptor的情况):RetryAndFollowUpInterceptor-->BridgeInterceptor-->CacheInterceptor-->ConnectInterceptor-->CallServerInterceptor。

RetryAndFollowUpInterceptor

RetryAndFollowUpInterceptor的作用主要是重试与重定向。
来看下它的Interceptor方法。

@Override public Response intercept(Chain chain) throws IOException {Request request = chain.request();//获取传入的chain的request 此处的request是next的requestRealInterceptorChain realChain = (RealInterceptorChain) chain;Transmitter transmitter = realChain.transmitter();int followUpCount = 0;Response priorResponse = null;while (true) {//开启一个无限循环transmitter.prepareToConnect(request);if (transmitter.isCanceled()) {//如果此时请求被cancel抛出异常throw new IOException("Canceled");}Response response;boolean success = false;try {response = realChain.proceed(request, transmitter, null);//调用next的proceed方法,即调用下一个Interceptorsuccess = true;} catch (RouteException e) {//如果通过某个route连接失败则尝试恢复。注意此时请求尚未发送出去// The attempt to connect via a route failed. The request will not have been sent.if (!recover(e.getLastConnectException(), transmitter, false, request)) {throw e.getFirstConnectException();}continue;} catch (IOException e) {//如果连接server失败则尝试恢复,注意此时请求已发送。// An attempt to communicate with a server failed. The request may have been sent.boolean requestSendStarted = !(e instanceof ConnectionShutdownException);if (!recover(e, transmitter, requestSendStarted, request)) throw e;continue;} finally {// The network call threw an exception. Release any resources.if (!success) {//执行不成功关闭transmitter.exchangeDoneDueToException();}}// Attach the prior response if it exists. Such responses never have a body.if (priorResponse != null) {//priorResponse不为空表示之前发生过重定向,此时为本次的response设置priorResponseresponse = response.newBuilder().priorResponse(priorResponse.newBuilder().body(null).build()).build();}Exchange exchange = Internal.instance.exchange(response);Route route = exchange != null ? exchange.connection().route() : null;Request followUp = followUpRequest(response, route);//生成重定向的请求if (followUp == null) {//followUp == null说明无需重定向,返回当前responeif (exchange != null && exchange.isDuplex()) {transmitter.timeoutEarlyExit();}return response;}RequestBody followUpBody = followUp.body();if (followUpBody != null && followUpBody.isOneShot()) {return response;}closeQuietly(response.body());if (transmitter.hasExchange()) {exchange.detachWithViolence();}if (++followUpCount > MAX_FOLLOW_UPS) {//判断是否超过最大重定向次数throw new ProtocolException("Too many follow-up requests: " + followUpCount);}request = followUp;priorResponse = response;}
}

来,梳理下整个流程:

  1. 开启一个无限循环,直至没有重定向或有异常抛出才会结束。
  2. 处理cancel
  3. 调用下一个Interceptor获取respone
  4. 如果步骤3发生异常,尝试恢复并重试请求
  5. 如果步骤3未发生异常为,priorResponse不为空(如果priorResponse表示之前发生过重定向),为当前respone设置priorResponse(注意priorResponse body是null)
  6. 调用followUpRequest根据返回的code生成用于重定向的请求
  7. 步骤6生成的请求为空表示无需重定向,返回当前respone,结束循环
  8. 步骤6生成的请求不为空表示需要重定向,此时重定向次数+1,然后判断是否超过最大重定向次数,未超过则跳转到步骤2开始下次循环。

步骤3是拦截器能链式有序调用的关键。
步骤4的判断是否能恢复是通过recover()函数,具体看注释

/*** Report and attempt to recover from a failure to communicate with a server. Returns true if* {@code e} is recoverable, or false if the failure is permanent. Requests with a body can only* be recovered if the body is buffered or if the failure occurred before the request has been* sent.*/
private boolean recover(IOException e, Transmitter transmitter,boolean requestSendStarted, Request userRequest) {// The application layer has forbidden retries.
//应用设置禁止重试 返回falseif (!client.retryOnConnectionFailure()) return false;// We can't send the request body again.
//request设置仅能执行一次 返回falseif (requestSendStarted && requestIsOneShot(e, userRequest)) return false;// This exception is fatal.
//异常是致命的 返回falseif (!isRecoverable(e, requestSendStarted)) return false;// No more routes to attempt.
//没有更多的route可供尝试 返回fasleif (!transmitter.canRetry()) return false;// For failure recovery, use the same route selector with a new connection.return true;
}

可以看出恢复操作是有条件的,在某些条件下是不能恢复的。另外上面说的不可恢复致命异常有ProtocolException、InterruptedIOException、SSLHandshakeException、CertificateException
、SSLPeerUnverifiedException。

步骤6的followUpRequest可以说是重定向的核心了,看下followUpRequest函数

private Request followUpRequest(Response userResponse, @Nullable Route route) throws IOException {if (userResponse == null) throw new IllegalStateException();int responseCode = userResponse.code();//获取当前respone的返回codefinal String method = userResponse.request().method();switch (responseCode) {//根据responseCode的值分情况处理case HTTP_PROXY_AUTH://407Proxy selectedProxy = route != null? route.proxy(): client.proxy();if (selectedProxy.type() != Proxy.Type.HTTP) {throw new ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy");}return client.proxyAuthenticator().authenticate(route, userResponse);//代理验证case HTTP_UNAUTHORIZED://401return client.authenticator().authenticate(route, userResponse);//身份认证case HTTP_PERM_REDIRECT:case HTTP_TEMP_REDIRECT:// "If the 307 or 308 status code is received in response to a request other than GET// or HEAD, the user agent MUST NOT automatically redirect the request"if (!method.equals("GET") && !method.equals("HEAD")) {//307、308 两种code不对 GET、HEAD 以外的请求重定向return null;}// fall-throughcase HTTP_MULT_CHOICE://300case HTTP_MOVED_PERM://301case HTTP_MOVED_TEMP://302case HTTP_SEE_OTHER://303
//以上这四种code是可以进行重定向的// Does the client allow redirects?if (!client.followRedirects()) return null;//客户端不允许重定向 返回nullString location = userResponse.header("Location");//获取Location以确定重定向目标if (location == null) return null;//Response的Location为null 返回nullHttpUrl url = userResponse.request().url().resolve(location);// Don't follow redirects to unsupported protocols.if (url == null) return null;// If configured, don't follow redirects between SSL and non-SSL.boolean sameScheme = url.scheme().equals(userResponse.request().url().scheme());if (!sameScheme && !client.followSslRedirects()) return null;// Most redirects don't include a request body.Request.Builder requestBuilder = userResponse.request().newBuilder();if (HttpMethod.permitsRequestBody(method)) {final boolean maintainBody = HttpMethod.redirectsWithBody(method);//是否带body重定向if (HttpMethod.redirectsToGet(method)) {requestBuilder.method("GET", null);} else {RequestBody requestBody = maintainBody ? userResponse.request().body() : null;requestBuilder.method(method, requestBody);}if (!maintainBody) {requestBuilder.removeHeader("Transfer-Encoding");requestBuilder.removeHeader("Content-Length");requestBuilder.removeHeader("Content-Type");}}// When redirecting across hosts, drop all authentication headers. This// is potentially annoying to the application layer since they have no// way to retain them.if (!sameConnection(userResponse.request().url(), url)) {requestBuilder.removeHeader("Authorization");}return requestBuilder.url(url).build();//返回构造的重定向requestcase HTTP_CLIENT_TIMEOUT://408 实际很少用到,一般需要重复发送一个相同的请求// 408's are rare in practice, but some servers like HAProxy use this response code. The// spec says that we may repeat the request without modifications. Modern browsers also// repeat the request (even non-idempotent ones.)if (!client.retryOnConnectionFailure()) {// The application layer has directed us not to retry the request.return null;}RequestBody requestBody = userResponse.request().body();if (requestBody != null && requestBody.isOneShot()) {return null;}if (userResponse.priorResponse() != null&& userResponse.priorResponse().code() == HTTP_CLIENT_TIMEOUT) {// We attempted to retry and got another timeout. Give up.return null;}if (retryAfter(userResponse, 0) > 0) {return null;}return userResponse.request();case HTTP_UNAVAILABLE://503if (userResponse.priorResponse() != null&& userResponse.priorResponse().code() == HTTP_UNAVAILABLE) {// We attempted to retry and got another timeout. Give up.return null;}if (retryAfter(userResponse, Integer.MAX_VALUE) == 0) {// specifically received an instruction to retry without delayreturn userResponse.request();}return null;default:return null;}
}

followUpRequest主要是根据返回的code分情况处理,如果允许重定向则返回新构造的request否则返回null表示不允许重定向。

BridgeInterceptor

BridgeInterceptor名为桥接拦截器主要作用就是把我们传入的request“重写”为实际向server请求的request,收到respone后“重写”respone。来看下其intercept函数

@Override public Response intercept(Chain chain) throws IOException {Request userRequest = chain.request();Request.Builder requestBuilder = userRequest.newBuilder();RequestBody body = userRequest.body();//获取请求bodyif (body != null) {//请求发送前“重写”headersMediaType 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// the transfer stream.boolean transparentGzip = false;if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {transparentGzip = true;requestBuilder.header("Accept-Encoding", "gzip");}List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());if (!cookies.isEmpty()) {//cookie不为空则添加cookierequestBuilder.header("Cookie", cookieHeader(cookies));}if (userRequest.header("User-Agent") == null) {//设置User-AgentrequestBuilder.header("User-Agent", Version.userAgent());}Response networkResponse = chain.proceed(requestBuilder.build());//开启下一个拦截器的调用HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());//从这里开始是收到respone然后对其“重写”Response.Builder responseBuilder = networkResponse.newBuilder().request(userRequest);if (transparentGzip&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))&& HttpHeaders.hasBody(networkResponse)) {//如果之前采用gzip进行压缩,那么需要对respone进行解压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)));}return responseBuilder.build();
}

BridgeInterceptor完成的工作可以分为3步:

  1. 请求发送前对request“重写”,重写后的request才是实际去用来请求的request。
  2. 调用chain.proceed开启下一个拦截器调用,并拿到respone
  3. 对返回的respone进行“重写”,我们拿到的respone就是重写后的

对request和respone的“重写”基本都是针对其headers,比如发送请求前未设置Accept-EncodingOkhttp会为你设置,在有cookie的情况下为你添加cookie,在拿到respone后如果需要解压缩Okhttp会为你自动解压缩。

CacheInterceptor

CacheInterceptor是缓存拦截器,主要作用是定义Okhttp的缓存机制。

@Override public Response intercept(Chain chain) throws IOException {Response cacheCandidate = cache != null? cache.get(chain.request()): null;//若当前有cache则根据请求获取对应的缓存long now = System.currentTimeMillis();//构造缓存策略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.//如果设置禁止从网络获取响应且缓存不可用那么返回失败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.//从cache获取respone不使用网络if (networkRequest == null) {return cacheResponse.newBuilder().cacheResponse(stripBody(cacheResponse)).build();}Response networkResponse = null;try {networkResponse = chain.proceed(networkRequest);//调用下一个拦截器,从网络获取respone} finally {// If we're crashing on I/O or otherwise, don't leak the cache body.if (networkResponse == null && cacheCandidate != null) {closeQuietly(cacheCandidate.body());}}// If we have a cache response too, then we're doing a conditional get.if (cacheResponse != null) {if (networkResponse.code() == HTTP_NOT_MODIFIED) {//从网络获取respone且缓存非空 如果返回码为304 则更新缓存然后返回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());}}//没有缓存可供使用,读取网络响应构造responeResponse response = networkResponse.newBuilder().cacheResponse(stripBody(cacheResponse)).networkResponse(stripBody(networkResponse)).build();if (cache != null) {//cache不为空 把respone缓存到cacheif (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;
}

整个流程如下:

  1. 判断是否有Cache,有的话根据request去尝试获取缓存
  2. 构造缓存策略
  3. 步骤1获取的缓存respone不为空但是不可用,关闭连接
  4. 设置禁止从网络获取响应且缓存不可用那么返回504失败
  5. 从cache获取respone 不使用网络(步骤1-5的作用就是在有请求时先尝试从本地获取缓存如果失败才会去从网络获取否则返回缓存)
  6. 调用下一个拦截器,从网络获取respone
  7. 从网络获取respone且缓存非空 如果返回码为304 则更新缓存然后返回
  8. 没有缓存可供使用,读取网络响应构造respone
  9. cache不为空 把respone缓存到cache(步骤6-9作用就是从网络获取respone然后把获得的respone缓存到本地)

CacheInterceptor中涉及两个类:Cache、CacheStrategy,这里先不展开分析,等分析Okhttp缓存机制时再做详细介绍。

ConnectInterceptor

ConnectInterceptor是连接拦截器,它的intercept函数非常简洁:

@Override public Response intercept(Chain chain) throws IOException {RealInterceptorChain realChain = (RealInterceptorChain) chain;Request request = realChain.request();Transmitter transmitter = realChain.transmitter();//获取Transmitter// We need the network to satisfy this request. Possibly for validating a conditional GET.boolean doExtensiveHealthChecks = !request.method().equals("GET");Exchange exchange = transmitter.newExchange(chain, doExtensiveHealthChecks);//构造Exchangereturn realChain.proceed(request, transmitter, exchange);//开启下一个拦截器调用并传入Transmitter、Exchange。
}

它首先获取Transmitter,然后通过Transmitter的newExchange方法创建一个Exchange,把它传到下一个拦截器。
Transmitter是应用和网络之间的一个桥梁,通过transmitter.newExchange构造一个Exchange实例

Exchange newExchange(Interceptor.Chain chain, boolean doExtensiveHealthChecks) {synchronized (connectionPool) {if (noMoreExchanges) {throw new IllegalStateException("released");}if (exchange != null) {throw new IllegalStateException("cannot make a new request because the previous response "+ "is still open: please call response.close()");}}ExchangeCodec codec = exchangeFinder.find(client, chain, doExtensiveHealthChecks);Exchange result = new Exchange(this, call, eventListener, exchangeFinder, codec);synchronized (connectionPool) {this.exchange = result;this.exchangeRequestDone = false;this.exchangeResponseDone = false;return result;}
}

newExchange主要做了两件事:调用ExchangeFinder.find获取一个ExchangeCodec、构造一个Exchange。
ExchangeFinder就是负责连接的创建,把创建好的连接放入连接池,如果连接池中已经有该连接,就直接取出复用。而ExchangeCodec则是对HTTP请求和HTTP响应编码
Exchange则是用来进行发送和接收HTTP request和respone。

CallServerInterceptor

CallServerInterceptor是拦截器链中最后一个拦截器,与服务器的交互如,发出请求和接收响应都是它完成的。

@Override public Response intercept(Chain chain) throws IOException {RealInterceptorChain realChain = (RealInterceptorChain) chain;Exchange exchange = realChain.exchange();Request request = realChain.request();long sentRequestMillis = System.currentTimeMillis();exchange.writeRequestHeaders(request);//向服务端写请求boolean responseHeadersStarted = false;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"))) {exchange.flushRequest();responseHeadersStarted = true;exchange.responseHeadersStart();responseBuilder = exchange.readResponseHeaders(true);}if (responseBuilder == null) {if (request.body().isDuplex()) {// Prepare a duplex body so that the application can send a request body later.exchange.flushRequest();BufferedSink bufferedRequestBody = Okio.buffer(exchange.createRequestBody(request, true));request.body().writeTo(bufferedRequestBody);} else {// Write the request body if the "Expect: 100-continue" expectation was met.BufferedSink bufferedRequestBody = Okio.buffer(exchange.createRequestBody(request, false));request.body().writeTo(bufferedRequestBody);bufferedRequestBody.close();}} else {exchange.noRequestBody();if (!exchange.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.exchange.noNewExchangesOnConnection();}}} else {exchange.noRequestBody();}if (request.body() == null || !request.body().isDuplex()) {exchange.finishRequest();}if (!responseHeadersStarted) {exchange.responseHeadersStart();//从服务端获取请求}if (responseBuilder == null) {responseBuilder = exchange.readResponseHeaders(false);}Response response = responseBuilder.request(request).handshake(exchange.connection().handshake()).sentRequestAtMillis(sentRequestMillis).receivedResponseAtMillis(System.currentTimeMillis()).build();int code = response.code();if (code == 100) {// server sent a 100-continue even though we did not request one.// try again to read the actual responseresponse = exchange.readResponseHeaders(false).request(request).handshake(exchange.connection().handshake()).sentRequestAtMillis(sentRequestMillis).receivedResponseAtMillis(System.currentTimeMillis()).build();code = response.code();}exchange.responseHeadersEnd(response);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(exchange.openResponseBody(response)).build();}if ("close".equalsIgnoreCase(response.request().header("Connection"))|| "close".equalsIgnoreCase(response.header("Connection"))) {exchange.noNewExchangesOnConnection();}if ((code == 204 || code == 205) && response.body().contentLength() > 0) {throw new ProtocolException("HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());}return response;
}

在ConnectInterceptor中我们已经与服务器建立了连接,获取了输入输出流,所以CallServerInterceptor的intercept(Chain)方法逻辑就是把请求发送到服务器,然后获取服务器的响应。

发送请求

通过Exchange的writeRequestHeaders(request)方法写入请求的header;如果请求的body不为空,通过okio写入请求的body。

获取响应

通过Exchange的readResponseHeaders(boolean)方法读取响应的header;通过Exchange的openResponseBody(Response)方法读取响应的body。

可以看出发送请求个获取响应都是通过exchange来进行的。

至此Okhttp的拦截器机制我们就分析完了,以上是Okhttp已经定义好的拦截器,在实际的使用中我们可以自定义拦截器来完成我们想要的功能。

# Okhttp解析—Interceptor详解相关推荐

  1. okhttp的应用详解与源码解析--http的发展史

    乘5G之势,借物联网之风,Android未来亦可期,Android优势在于开放,手机.平板.车载设备.智能家居等都是Android的舞台,Google不倒,Android不灭,本专栏的同步视频教程已经 ...

  2. 安卓 linux init.rc,[原创]Android init.rc文件解析过程详解(二)

    Android init.rc文件解析过程详解(二) 3.parse_new_section代码如下: void parse_new_section(struct parse_state *state ...

  3. Android init.rc文件解析过程详解(三)

    Android init.rc文件解析过程详解(三) 三.相关结构体 1.listnode listnode结构体用于建立双向链表,这种结构广泛用于kernel代码中, android源代码中定义了l ...

  4. Android init.rc文件解析过程详解(二)

    Android init.rc文件解析过程详解(二) 3.parse_new_section代码如下: void parse_new_section(struct parse_state *state ...

  5. Android init.rc文件解析过程详解(一)

        Android init.rc文件解析过程详解(一) 一.init.rc文件结构介绍 init.rc文件基本组成单位是section, section分为三种类型,分别由三个关键字(所谓关键字 ...

  6. 加载vue文件步骤_vue中.vue文件解析步骤详解

    这次给大家带来vue中.vue文件解析步骤详解,vue中.vue文件解析的注意事项有哪些,下面就是实战案例,一起来看一下. 我们平时写的 .vue 文件称为 SFC(Single File Compo ...

  7. python自动解析json_Python语言解析JSON详解

    本文主要向大家介绍了Python语言解析JSON详解,通过具体的内容向大家展示,希望对大家学习Python语言有所帮助. JSON 函数使用 JSON 函数需要导入 json 库:import jso ...

  8. 阿里云物联网平台-数据解析脚本详解

    阿里云物联网平台-数据解析脚本详解 var COMMAND_REPORT = 0x00; //属性上报. var COMMAND_SET = 0x01; //属性设置. var COMMAND_REP ...

  9. linux中etc下的hosts(本地IP解析)文件详解

    linux中etc下的hosts(本地IP解析)文件详解 1./etc/hosts(本地解析) 很多人一提到更改hostname首先就想到修改/etc/hosts文件, 认为hostname的配置文件 ...

  10. python 元组拆包_Python元组拆包和具名元组解析实例详解

    前言 在Python中元组是一个相较于其他语言比较特别的一个内置序列类型.有些python入门教程把元组成为"不可变的列表",这种说法是不完备的,其并没有完整的概括元组的特点.除了 ...

最新文章

  1. Linux进程及进程管理命令
  2. 008_HttpServletRequest对象
  3. ACM入门之【约数】
  4. 去重和分类后缀asp、php等路径 用python3写的
  5. 第一阶段_第三部分_光照与GI
  6. 田牌魔术 | .NET Core 3.0 + Azure 远程点亮树莓派上的一盏灯
  7. Hadoop的学习路线图
  8. 【软件工程】用于IS规划的SWOT方法
  9. jquery学习系列8(过滤选择器)
  10. 【5分钟 Paper】Playing Atari with Deep Reinforcement Learning
  11. day 21 模块 和 包
  12. proxmoxve打造云桌面_[pve][vdi]用deskpool创建基于proxmoxVE的桌面云
  13. linux-2.6.34.1移植到TQ2440
  14. zotero+坚果云实现多pc端及iPad同步管理查看文献【保姆教程】
  15. 蓝桥杯2014省赛——猜年龄(Java)
  16. 开源生态学初探——从生命游戏开始
  17. 【优化求解】粒子群优化和重力搜索算法求解MLP问题matlab源码
  18. 蔚来事故背后真相:Pilot只是舒适性功能,NOP仅是公开测试版本
  19. 【大数据存储技术】第7章 MongoDB 的原理和使用
  20. 三菱PLC输出指示灯输出模块不亮怎么解决

热门文章

  1. 酒店返现应用评测: 企鹅竟然没有模仿?
  2. martyr2s提出的计算机练手小项目(未翻译)
  3. C语言基础100例子
  4. c语言SPF算法代码,SPF 算法具体过程
  5. 携程数据开发2022留用实习面试
  6. python数据分析之航空公司客户价值分析
  7. hbuilder设置html格式,Hbuilder中如何设置格式化CSS代码为单行
  8. jmeter--解决登录接口只执行一次和多接口依赖的问题(使用事务控制器和循环控制器)
  9. 红米note5解锁教程_红米NOTE5解锁包
  10. 楼天城楼教主的acm心路历程(作为励志用)