以前曾经写过一篇关于Okhttp的使用的文章深入解析OkHttp3,通过这篇文章可以了解OkHttp的各种基本用法,光会使用并不算好汉,我们还要深入理解源码,学习优秀的设计思想,本篇我就带大家一起分析源码,基于Okhttp 3.10.0版本。
1.1.1 发送同步请求
Request request = new Request.Builder().url(url).build();try {//同步请求Call call = wCall(request);Response response = ute();String json = response.body().string();Log.d(TAG, json);} catch (IOException e) {e.printStackTrace();}
1.1.2 发送异步请求
//异步请求Call call = wCall(request);queue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {Log.d(TAG, "onFailure:" + e.getMessage());}@Overridepublic void onResponse(Call call, Response response) throws IOException {String json = response.body().string();Log.d(TAG, json);}});
首先,我们要理解,无论是同步请求还是异步请求,我们都需要先编写以下代码:
OkHttpClient mOkHttpClient = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
Call call = wCall(request);
Call是请求的关键对象,是通过调用Call的execute方法之后,就会进入请求的逻辑
Response response = ute();
1.2.1 OkHttpClient# newCall
@Override public Call newCall(Request request) {wRealCall(this, request, false /* for web socket */);}
我们可以看到OkHttpClient# newCall会调用wRealCall方法
1.2.2 RealCall # newRealCall
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {// Safely publish the Call instance to the EventListener.RealCall call = new RealCall(client, originalRequest, forWebSocket);call.eventListener = client.eventListenerFactory().create(call);return call;}
查看一下构造函数
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {this.client = iginalRequest = originalRequest;this.forWebSocket = AndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);}
RealCall的创建过程中会持有OkHttpClient,请求的 Request还有创建了一个拦截器RetryAndFollowUpInterceptor(这个后面会详细说明),同时创建eventListener 。
1.2.3 Call# execute()
创建好Call之后,调用execute()方法就开始了请求的流程,Call是一个借口,所以我们要查看它的实现类RealCall。
1.2.4 RealCall# execute()
@Override public Response execute() throws IOException {synchronized (this) {if (executed) throw new IllegalStateException("Already Executed");executed = true;}captureCallStackTrace();eventListener.callStart(this);try {client.dispatcher().executed(this);Response result = getResponseWithInterceptorChain();if (result == null) throw new IOException("Canceled");return result;} catch (IOException e) {eventListener.callFailed(this, e);throw e;} finally {client.dispatcher().finished(this);}}
这里重点在于如下两句
client.dispatcher().executed(this);Response result = getResponseWithInterceptorChain();
调用OkHttpClient持有的Dispatcher对象执行call,Dispatcher是非常重要的一环,后面详细介绍。
1.2.5 Dispatcher# execute()
synchronized void executed(RealCall call) {runningSyncCalls.add(call);}
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
将同步请求的RealCall 添加到同步的队列中。
1.2.6 RealCall# getResponseWithInterceptorChain()
通过RealCall# getResponseWithInterceptorChain()方法就可以获取请求返回的Response,返回到调用者
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 kieJar()));interceptors.add(new CacheInterceptor(client.internalCache()));interceptors.add(new ConnectInterceptor(client));if (!forWebSocket) {interceptors.addAll(clientworkInterceptors());}interceptors.add(new CallServerInterceptor(forWebSocket));Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,originalRequest, this, eventListener, tTimeoutMillis(),adTimeoutMillis(), client.writeTimeoutMillis());return chain.proceed(originalRequest);}
这里主要是一系列拦截器的添加操作,然后调用Interceptor.Chain的proceed方法去执行请求
chain.proceed(originalRequest)
拦截器又是另一个非常重要的环节,后面重点提到。
1.2.7总结同步请求
同步请求逻辑相对简单,通过Call# execute()最终会调用 RealCall# execute(),然后通过分发器Dispatcher将任务添加到同步队列中,然后通过一系列拦截器操作后进行请求,最后返回Response,全程都在主线程中运行,是阻塞式的。
1.3.1 Call#enqueue
异步请求,会调用Call#enqueue方法,因为是异步,所以需要传递一个Callback回调,Call#enqueue中调用了RealCall#enqueue
1.3.2 RealCall#enqueue
@Override public void enqueue(Callback responseCallback) {synchronized (this) {if (executed) throw new IllegalStateException("Already Executed");executed = true;}captureCallStackTrace();eventListener.callStart(this);client.dispatcher().enqueue(new AsyncCall(responseCallback));}
RealCall#enqueue中同样会调用到Dispatcher中,只是调用enqueue方法,同时new AsyncCall将Callback 包一层.AsyncCall是RealCall的内部类,从中可以获取RealCall的Request等成员。
1.3.2 Dispatcher#enqueue
synchronized void enqueue(AsyncCall call) {if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {runningAsyncCalls.add(call);executorService().execute(call);} else {readyAsyncCalls.add(call);}}
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();/** Running synchronous calls. Includes canceled calls that haven't finished yet. */private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
主要做了几件事:
1.判断正在执行的异步任务队列中任务数是否小于maxRequests,且正在执行的任务的host小于 maxRequestsPerHost,这两个值的大小为:
private int maxRequests = 64;private int maxRequestsPerHost = 5;
同时满足条件,则将AsyncCall 添加到runningAsyncCalls队列中,runningAsyncCalls是异步任务的队列,否则添加到readyAsyncCalls等待队列中。
2.调用Dispatcher#executorService方法,获取Android系统提供的线程池
public synchronized ExecutorService executorService() {if (executorService == null) {executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));}return executorService;}
**注意:**这里设置线程池的最大容量为 Integer.MAX_VALUE,但其实受限于maxRequests,所以最多容量也就64而已。
3.通过ExecutorService执行AsyncCall任务,可想而知AsyncCall一定是实现了Runnable接口。
1.3.3 NamedRunnable#run
AsyncCall继承自NamedRunnable,所以当AsyncCall任务执行时,会执行NamedRunnable#run
@Override public final void run() {String oldName = Thread.currentThread().getName();Thread.currentThread().setName(name);try {execute();} finally {Thread.currentThread().setName(oldName);}}
主要逻辑在execute()方法
1.3.4 AsyncCall#execute
@Override protected void execute() {boolean signalledCallback = false;try {Response response = getResponseWithInterceptorChain();if (retryAndFollowUpInterceptor.isCanceled()) {signalledCallback = Failure(RealCall.this, new IOException("Canceled"));} else {signalledCallback = Response(RealCall.this, response);}} catch (IOException e) {if (signalledCallback) {// Do not signal the callback ().log(INFO, "Callback failure for " + toLoggableString(), e);} else {eventListener.callFailed(RealCall.this, e);Failure(RealCall.this, e);}} finally {client.dispatcher().finished(this);}}
可以看到还是会和同步请求一样,调用getResponseWithInterceptorChain()执行各种拦截器,返回Response,无论是同步还是异步最后都会执行Dispatcher#inished()方法,这个后面会提到。
1.3.5 总结异步请求
Call#enqueue会调用到 Dispatcher#enqueue,然后判断是否符合最大请求数maxRequests(64),最大请求Host数maxRequestsPerHost (5),符合条件的添加到异步任务队列runningAsyncCalls,通过线程池执行任务,否则添加到等待队列readyAsyncCalls。
前面分析同步和异步请求的时候,都提到Dispatcher,我们这里重新总结一下:
1.维护了3个队列,同步请求执行队列runningSyncCalls,异步请求执行队列runningAsyncCalls,异步请求等待队列readyAsyncCalls,3个队列的添加逻辑前面已经提过。
/** Ready async calls in the order they'll be run. */private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();/** Running synchronous calls. Includes canceled calls that haven't finished yet. */private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
2.维护了异步请求的线程池,异步执行任务通过线程池进行任务执行
public synchronized ExecutorService executorService() {if (executorService == null) {executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));}return executorService;}
3.RealCall中请求任务执行完后,进行回收,我们知道Call会被封装成RealCall,但无论同步还是异步执行完成后,都会调用以下代码
finally {client.dispatcher().finished(this);}
分为同步和异步的finished,我们先看同步的代码:
2.1.1 同步finished
/** Used by {@code Call#execute} to signal completion. */void finished(RealCall call) {finished(runningSyncCalls, call, false);}
注意这里传入的第三个参数为false
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {int runningCallsCount;Runnable idleCallback;synchronized (this) {if (!ve(call)) throw new AssertionError("Call wasn't in-flight!");if (promoteCalls) promoteCalls();runningCallsCount = runningCallsCount();idleCallback = this.idleCallback;}if (runningCallsCount == 0 && idleCallback != null) {idleCallback.run();}}
1.首先将队列中call进行移除
2.如果promoteCalls为true还会调用 promoteCalls()方法
3.计算runningCallsCount,即为同步和异步执行队列的size总和
public synchronized int runningCallsCount() {return runningAsyncCalls.size() + runningSyncCalls.size();}
4.当runningCallsCount为0时说明已经没有任务了,进行回调
if (runningCallsCount == 0 && idleCallback != null) {idleCallback.run();}
void finished(AsyncCall call) {finished(runningAsyncCalls, call, true);}
2.1.2 异步finished
异步代码如下:
void finished(AsyncCall call) {finished(runningAsyncCalls, call, true);}
和同步的区别是,传入的promoteCalls为true,所以当执行finished时会比同步多执行一个promoteCalls()方法
Dispatcher#promoteCalls
private void promoteCalls() {if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {AsyncCall call = i.next();if (runningCallsForHost(call) < maxRequestsPerHost) {i.remove();runningAsyncCalls.add(call);executorService().execute(call);}if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.}}
逻辑很清晰,就是当异步执行队列readyAsyncCalls有空闲位置时,遍历等待队列,将readyAsyncCalls的任务取出加入readyAsyncCalls,然后线程池对任务进行执行。
在之前分析同步和异步任务的时候,分析过getResponseWithInterceptorChain()方法执行后就会返回请求结果Response
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 kieJar()));interceptors.add(new CacheInterceptor(client.internalCache()));interceptors.add(new ConnectInterceptor(client));if (!forWebSocket) {interceptors.addAll(clientworkInterceptors());}interceptors.add(new CallServerInterceptor(forWebSocket));Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,originalRequest, this, eventListener, tTimeoutMillis(),adTimeoutMillis(), client.writeTimeoutMillis());return chain.proceed(originalRequest);}
1.首先将调用者自定义的拦截器都放入interceptors集合的最前面,然后是分别添加okhttp中必须的几个拦截器,后面我们会一一分析
2.创建拦截器的链RealInterceptorChain,将interceptors传入
从上述代码可以看出拦截器调用的先后顺序依次是
client.interceptors()–>RetryAndFollowUpInterceptor–>BridgeInterceptor–>CacheInterceptor–>ConnectInterceptor–>clientworkInterceptors()–>CallServerInterceptor
这里使用到了非常经典的设计模式,就是责任链模式,reques自上而下下传递执行,然后Response至下而上返回
这里3个参数是为 null的
@Override public Response proceed(Request request) throws IOException {return proceed(request, streamAllocation, httpCodec, connection);}
关键部分代码如下:
// Call the next interceptor in the chain.RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,writeTimeout);Interceptor interceptor = (index);Response response = interceptor.intercept(next);
又创建了一个RealInterceptorChain,然后获取interceptors中第一位的拦截器开始执行,这里index=0。然后就会按照顺序执行各拦截器。
如果没有自定义拦截器情况下,首先会走到RetryAndFollowUpInterceptor的intercept方法
@Override public Response intercept(Chain chain) throws IOException {Request request = quest();RealInterceptorChain realChain = (RealInterceptorChain) chain;Call call = realChain.call();EventListener eventListener = realChain.eventListener();StreamAllocation streamAllocation = new tionPool(),createAddress(request.url()), call, eventListener, callStackTrace);this.streamAllocation = streamAllocation;int followUpCount = 0;Response priorResponse = null;while (true) {if (canceled) {lease();throw new IOException("Canceled");}Response response;boolean releaseConnection = true;try {response = realChain.proceed(request, streamAllocation, null, null);releaseConnection = false;} catch (RouteException e) {//省略} catch (IOException e) {//省略} finally {// We're throwing an unchecked exception. Release any resources.if (releaseConnection) {streamAllocation.streamFailed(null);lease();}}// Attach the prior response if it exists. Such responses never have a body.if (priorResponse != null) {response = wBuilder().wBuilder().body(null).build()).build();}Request followUp = followUpRequest(response, ute());if (followUp == null) {if (!forWebSocket) {lease();}return response;}closeQuietly(response.body());if (++followUpCount > MAX_FOLLOW_UPS) {lease();throw new ProtocolException("Too many follow-up requests: " + followUpCount);}if (followUp.body() instanceof UnrepeatableRequestBody) {lease();throw new HttpRetryException("Cannot retry streamed HTTP body", de());}if (!sameConnection(response, followUp.url())) {lease();streamAllocation = new tionPool(),createAddress(followUp.url()), call, eventListener, callStackTrace);this.streamAllocation = streamAllocation;} else if (dec() != null) {throw new IllegalStateException("Closing the body of " + response+ " didn't close its backing stream. Bad interceptor?");}request = followUp;priorResponse = response;}}
1.创建StreamAllocation
StreamAllocation streamAllocation = new tionPool(),createAddress(request.url()), call, eventListener, callStackTrace);this.streamAllocation = streamAllocation;
主要传入OkHttpClient中的ConnectionPool,还有通过请求request.url()创建出Address对象,主要是HTTP请求中一些SSLSocket,host认证,Dns等
try {response = realChain.proceed(request, streamAllocation, null, null);releaseConnection = false;}
realChain执行proceed方法,此时streamAllocation已经有值传入,此时再次进入RealInterceptorChain#proceed方法中
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,RealConnection connection) throws IOException {if (index >= interceptors.size()) throw new AssertionError();calls++;//省略// Call the next interceptor in the chain.RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,writeTimeout);Interceptor interceptor = (index);Response response = interceptor.intercept(next);//省略return response;}
}
这里再次进来index已经变为1,然后又再新创建一个RealInterceptorChain,从interceptors中取出下一个拦截器,执行下一个拦截器的逻辑
**总结一下:**这里责任链模式,每一个拦截器执行时都会创建一个拦截器链RealInterceptorChain,index也会随之增加1,这样在 (index)中就会取出下一个拦截器,一直向下执行到没有拦截器为止,同时每一个拦截的Response是下一个拦截器执行的返回的结果
RetryAndFollowUpInterceptor最重要的是创建了StreamAllocation
BridgeInterceptor#intercept
@Override public Response intercept(Chain chain) throws IOException {Request userRequest = quest();Request.Builder requestBuilder = wBuilder();RequestBody body = userRequest.body();if (body != null) {MediaType contentType = tType();if (contentType != null) {requestBuilder.header("Content-Type", String());}long contentLength = tLength();if (contentLength != -1) {requestBuilder.header("Content-Length", String(contentLength));veHeader("Transfer-Encoding");} else {requestBuilder.header("Transfer-Encoding", "chunked");veHeader("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()) {requestBuilder.header("Cookie", cookieHeader(cookies));}if (userRequest.header("User-Agent") == null) {requestBuilder.header("User-Agent", Version.userAgent());}Response networkResponse = chain.proceed(requestBuilder.build());iveHeaders(cookieJar, userRequest.url(), networkResponse.headers());Response.Builder responseBuilder = wBuilder().request(userRequest);if (transparentGzip&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))&& HttpHeaders.hasBody(networkResponse)) {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拦截器的作用主要是添加一些网络请求的必备参数,例如Content-Type,Content-Length,Host,ConnectionAccept-Encoding,Cookie,User-Agent等,如果有使用gzip的话,还会进行gzip的处理
@Override public Response intercept(Chain chain) throws IOException {Response cacheCandidate = cache != null? (quest()): null;long now = System.currentTimeMillis();CacheStrategy strategy = new CacheStrategy.Factory(now, quest(), cacheCandidate).get();Request networkRequest = strategyworkRequest;Response cacheResponse = strategy.cacheResponse;if (cache != null) {ackResponse(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().quest()).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) {wBuilder().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());}}// If we have a cache response too, then we're doing a conditional get.if (cacheResponse != null) {if (de() == HTTP_NOT_MODIFIED) {Response response = wBuilder().headers(combine(cacheResponse.headers(), networkResponse.headers())).sentRequestAtMillis(networkResponse.sentRequestAtMillis()).ivedResponseAtMillis()).cacheResponse(stripBody(cacheResponse))workResponse(stripBody(networkResponse)).build();networkResponse.body().close();// Update the cache after combining headers but before stripping the// Content-Encoding header (as performed by initContentStream()).ackConditionalCacheHit();cache.update(cacheResponse, response);return response;} else {closeQuietly(cacheResponse.body());}}Response response = wBuilder().cacheResponse(stripBody(cacheResponse))workResponse(stripBody(networkResponse)).build();if (cache != null) {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.hod())) {try {ve(networkRequest);} catch (IOException ignored) {// The cache cannot be written.}}}return response;}
1.先从cache中以quest()为key获取缓存的Response,这里的request就是外部调用时创建的,这里的cache是InternalCache,实现类为okhttp3.Cache,查看get方法
@NullableResponse get(Request request) {String key = key(request.url());Snapshot snapshot;try {snapshot = (key);if (snapshot == null) {return null;}} catch (IOException var7) {return null;}Cache.Entry entry;try {entry = new Cache.Source(0));} catch (IOException var6) {Util.closeQuietly(snapshot);return null;}Response response = sponse(snapshot);if (!entry.matches(request, response)) {Util.closeQuietly(response.body());return null;} else {return response;}}
1.根据请求的url,进行计算获得一个key
2.在内部cache中通过key看有没保存的快照Snapshot。这里cache是采用了DiskLruCache的算法
3.如果Snapshot不为空,通过Snapshot创建出Cache.Entry,查看一下Cache.Entry的组成
其实就是存储了一些请求返回的信息
4.通过sponse方法获取缓存中的Response
public Response response(Snapshot snapshot) {String contentType = ("Content-Type");String contentLength = ("Content-Length");Request cacheRequest = (new okhttp3.Request.Builder()).url(this.url).questMethod, (RequestBody)null).headers(this.varyHeaders).build();return (new okhttp3.Response.Builder()).request(cacheRequest).protocol(this.protocol).de).ssage).sponseHeaders).body(new Cache.CacheResponseBody(snapshot, contentType, contentLength)).handshake(this.handshake).sentRequestAtMillis(this.sentRequestMillis).ivedResponseMillis).build();}
通过缓存得参数构造Request ,然后通过Request再创建出Response
5.校验缓存中的请求和相应是否和传入的Request所关联的一致
entry.matches(request, response)
6.将请求链chain中的request和缓存Response构造出一个CacheStrategy
CacheStrategy strategy = new CacheStrategy.Factory(now, quest(), cacheCandidate).get();
其实就是从缓存的cacheResponse中取出一些值进行赋值
public CacheStrategy get() {CacheStrategy candidate = getCandidate();if (candidateworkRequest != null && request.cacheControl().onlyIfCached()) {// We're forbidden from using the network and the cache urn new CacheStrategy(null, null);}return candidate;}
主要逻辑在getCandidate方法中
private CacheStrategy getCandidate() {// No cached response.if (cacheResponse == null) {return new CacheStrategy(request, null);}// Drop the cached response if it's missing a required handshake.if (request.isHttps() && cacheResponse.handshake() == null) {return new CacheStrategy(request, null);}// If this response shouldn't have been stored, it should never be used// as a response source. This check should be redundant as long as the// persistence store is well-behaved and the rules are constant.if (!isCacheable(cacheResponse, request)) {return new CacheStrategy(request, null);}CacheControl requestCaching = request.cacheControl();if (Cache() || hasConditions(request)) {return new CacheStrategy(request, null);}CacheControl responseCaching = cacheResponse.cacheControl();if (responseCaching.immutable()) {return new CacheStrategy(null, cacheResponse);}long ageMillis = cacheResponseAge();long freshMillis = computeFreshnessLifetime();if (requestCaching.maxAgeSeconds() != -1) {freshMillis = Math.min(freshMillis, Millis(requestCaching.maxAgeSeconds()));}long minFreshMillis = 0;if (requestCaching.minFreshSeconds() != -1) {minFreshMillis = Millis(requestCaching.minFreshSeconds());}long maxStaleMillis = 0;if (!responseCaching.mustRevalidate() && requestCaching.maxStaleSeconds() != -1) {maxStaleMillis = Millis(requestCaching.maxStaleSeconds());}if (!Cache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {Response.Builder builder = wBuilder();if (ageMillis + minFreshMillis >= freshMillis) {builder.addHeader("Warning", "110 HttpURLConnection "Response is stale"");}long oneDayMillis = 24 * 60 * 60 * 1000L;if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {builder.addHeader("Warning", "113 HttpURLConnection "Heuristic expiration"");}return new CacheStrategy(null, builder.build());}// Find a condition to add to the request. If the condition is satisfied, the response body// will not be transmitted.String conditionName;String conditionValue;if (etag != null) {conditionName = "If-None-Match";conditionValue = etag;} else if (lastModified != null) {conditionName = "If-Modified-Since";conditionValue = lastModifiedString;} else if (servedDate != null) {conditionName = "If-Modified-Since";conditionValue = servedDateString;} else {return new CacheStrategy(request, null); // No condition! Make a regular request.}Headers.Builder conditionalRequestHeaders = request.headers().newBuilder();Internal.instance.addLenient(conditionalRequestHeaders, conditionName, conditionValue);Request conditionalRequest = wBuilder().headers(conditionalRequestHeaders.build()).build();return new CacheStrategy(conditionalRequest, cacheResponse);}
getCandidate方法决定CacheStrategy的构成,一般会有如下一些情况:
noCache :不使用缓存,全部走网络
noStore : 不使用缓存,也不存储缓存
onlyIfCached : 只使用缓存
maxAge :设置最大失效时间,失效则不使用
maxStale :设置最大失效时间,失效则不使用
minFresh :设置最小有效时间,失效则不使用
FORCE_NETWORK : 强制走网络
FORCE_CACHE :强制走缓存
可以发现CacheStrategy中cacheResponse为null空有几种情况
1)没有缓存的response
2)如果这个请求是https的,但上次缓存的cacheResponse没有TLS handshake
3.通过isCacheable判断,一些请求返回值不符合要求的不缓存,还有就是请求头中有配置no-store参数时
4.请求头中声明了“no-cache”,或者“If-Modified-Since”,“If-None-Match”(服务器缓存)
5.请求头中没有添加任何条件时候
继续看 get()方法
if (candidateworkRequest != null && request.cacheControl().onlyIfCached()) {// We're forbidden from using the network and the cache urn new CacheStrategy(null, null);}
如果外部设置了onlyIfCached(只读缓存),但缓存又无效,那就构造的CacheStrategy中既没有request也没有request
// If we're forbidden from using the network and the cache is insufficient, fail.if (networkRequest == null && cacheResponse == null) {return new Response.Builder().quest()).protocol(Protocol.HTTP_1_1).code(504).message("Unsatisfiable Request (only-if-cached)").body(Util.EMPTY_RESPONSE).sentRequestAtMillis(-1L).receivedResponseAtMillis(System.currentTimeMillis()).build();}
1.如果设置了only-if-cached,只读缓存,但又没有缓存的Response,那就返回504
// If we don't need the network, we're done.if (networkRequest == null) {wBuilder().cacheResponse(stripBody(cacheResponse)).build();}
2workRequest == null这里表示只用缓存,不用网络请求,那就将缓存返回
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());}}
3workRequest !=null,那就调用接下来的拦截器进行请求,返回Response
if (cacheResponse != null) {if (de() == HTTP_NOT_MODIFIED) {Response response = wBuilder().headers(combine(cacheResponse.headers(), networkResponse.headers())).sentRequestAtMillis(networkResponse.sentRequestAtMillis()).ivedResponseAtMillis()).cacheResponse(stripBody(cacheResponse))workResponse(stripBody(networkResponse)).build();networkResponse.body().close();// Update the cache after combining headers but before stripping the// Content-Encoding header (as performed by initContentStream()).ackConditionalCacheHit();cache.update(cacheResponse, response);return response;} else {closeQuietly(cacheResponse.body());}}
4.如果旧的cacheResponse不为null,又通过网络请求返回操作码304,则将新的response更新
if (cache != null) {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.hod())) {try {ve(networkRequest);} catch (IOException ignored) {// The cache cannot be written.}}}
5.新的请求进行缓存,然后过期缓存进行移除
ConnectInterceptor#intercept
@Override public Response intercept(Chain chain) throws IOException {RealInterceptorChain realChain = (RealInterceptorChain) chain;Request request = quest();StreamAllocation streamAllocation = realChain.streamAllocation();// We need the network to satisfy this request. Possibly for validating a conditional GET.boolean doExtensiveHealthChecks = !hod().equals("GET");HttpCodec httpCodec = wStream(client, chain, doExtensiveHealthChecks);RealConnection connection = tion();return realChain.proceed(request, streamAllocation, httpCodec, connection);}
从链中获取出StreamAllocation,通过wStream方法返回一个HttpCodec,HttpCodec的作用是对请求进行编码,然后对响应进行解码
public HttpCodec newStream(OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {int connectTimeout = tTimeoutMillis();int readTimeout = adTimeoutMillis();int writeTimeout = chain.writeTimeoutMillis();int pingIntervalMillis = client.pingIntervalMillis();boolean connectionRetryEnabled = OnConnectionFailure();try {RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);HttpCodec resultCodec = wCodec(client, chain, this);synchronized (connectionPool) {codec = resultCodec;return resultCodec;}} catch (IOException e) {throw new RouteException(e);}}
通过findHealthyConnection方法创建一个RealConnection,此类负责https连接的主要工作,RealConnection#newCodec方法可以创建出HttpCodec,并返回。
通过findConnection方法创建RealConnection,如果这个RealConnection是全新的连接,就跳过连接健康检查,如果是之前已经连接过的RealConnection,则判断是不是一个健康的连接,如果否的话就将其从连接池connectionPool中进行回收。接下来看findConnection方法做了什么
......
if (result == null) {// Attempt to get a connection from the pool.(connectionPool, address, this, null);if (connection != null) {foundPooledConnection = true;result = connection;} else {selectedRoute = route;}}......
方法从连接池中获取是否有复用的连接,Internal是一个接口,它的实现在OkHttpClient的内部类中
1.OkHttpClient.Internal.instance#get
@Override public RealConnection get(ConnectionPool pool, Address address,StreamAllocation streamAllocation, Route route) {(address, streamAllocation, route);}
2.ConnectionPool#get
@Nullable RealConnection get(Address address, StreamAllocation streamAllocation, Route route) {assert (Thread.holdsLock(this));for (RealConnection connection : connections) {if (connection.isEligible(address, route)) {streamAllocation.acquire(connection, true);return connection;}}return null;}
在连接池中找出能匹配Address 的连接,注意这里的route传进来为null,connection.isEligible方法判断连接池中connection是否可复用,主要通过判断请求连接的host是否一致,具体逻辑在RealConnection#isEligible中
3.RealConnection#isEligible
public boolean isEligible(Address address, @Nullable Route route) {// If this connection is not accepting new streams, we're done.if (allocations.size() >= allocationLimit || noNewStreams) return false;// If the non-host fields of the address don't overlap, we're done.if (!Internal.instance.ute.address(), address)) return false;// If the host exactly matches, we're done: this connection can carry the address.if (address.url().host().ute().address().url().host())) {return true; // This connection is a perfect match.}// At this point we don't have a hostname match. But we still be able to carry the request if// our connection coalescing requirements are met. See also:// // /// 1. This connection must be HTTP/2.if (http2Connection == null) return false;// 2. The routes must share an IP address. This requires us to have a DNS address for both// hosts, which only happens after route planning. We can't coalesce connections that use a// proxy, since proxies don't tell us the origin server's IP address.if (route == null) return false;if (route.proxy().type() != Proxy.Type.DIRECT) return false;if (ute.proxy().type() != Proxy.Type.DIRECT) return false;if (!ute.socketAddress().equals(route.socketAddress())) return false;// 3. This connection's server certificate's must cover the new host.if (route.address().hostnameVerifier() != OkHostnameVerifier.INSTANCE) return false;if (!supportsUrl(address.url())) return false;// 4. Certificate pinning must match {ificatePinner().check(address.url().host(), handshake().peerCertificates());} catch (SSLPeerUnverifiedException e) {return false;}return true; // The caller's address can be carried by this connection.}
通过streamAllocation.acquire(connection, true),将connection和StreamAllocation相关联
4.StreamAllocation#acquire
public void acquire(RealConnection connection, boolean reportedAcquired) {assert (Thread.holdsLock(connectionPool));if (tion != null) throw new IllegalStateException();tion = portedAcquired = reportedAcquired;connection.allocations.add(new StreamAllocationReference(this, callStackTrace));}
在这里将connection赋值给了StreamAllocation,然后connection有一个集合存放与其关联的StreamAllocation,这里StreamAllocationReference是一个弱引用。
RealConnection connection = tion();return realChain.proceed(request, streamAllocation, httpCodec, connection);
通过streamAllocation获取出connection,继续传递给下一个拦截器
这个是Okhttp中自带的最后一个拦截器
@Override public Response intercept(Chain chain) throws IOException {RealInterceptorChain realChain = (RealInterceptorChain) chain;HttpCodec httpCodec = realChain.httpStream();StreamAllocation streamAllocation = realChain.streamAllocation();RealConnection connection = (RealConnection) tion();Request request = quest();long sentRequestMillis = System.currentTimeMillis();realChain.eventListener().requestHeadersStart(realChain.call());httpCodec.writeRequestHeaders(request);realChain.eventListener().requestHeadersEnd(realChain.call(), request);Response.Builder responseBuilder = null;if (HttpMethod.hod()) && 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"))) {httpCodec.flushRequest();realChain.eventListener().responseHeadersStart(realChain.call());responseBuilder = adResponseHeaders(true);}if (responseBuilder == null) {// Write the request body if the "Expect: 100-continue" expectation alChain.eventListener().requestBodyStart(realChain.call());long contentLength = request.body().contentLength();CountingSink requestBodyOut =new ateRequestBody(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 NewStreams();}}httpCodec.finishRequest();if (responseBuilder == null) {realChain.eventListener().responseHeadersStart(realChain.call());responseBuilder = adResponseHeaders(false);}Response response = quest(request).tion().handshake()).sentRequestAtMillis(sentRequestMillis).receivedResponseAtMillis(System.currentTimeMillis()).build();int code = de();if (code == 100) {// server sent a 100-continue even though we did not request one.// try again to read the actual responseresponseBuilder = adResponseHeaders(false);response = quest(request).tion().handshake()).sentRequestAtMillis(sentRequestMillis).receivedResponseAtMillis(System.currentTimeMillis()).build();code = de();}realChain.eventListener().responseHeadersEnd(realChain.call(), response);if (forWebSocket && code == 101) {// Connection is upgrading, but we need to ensure interceptors see a non-null sponse = wBuilder().body(Util.EMPTY_RESPONSE).build();} else {response = wBuilder().body(httpCodec.openResponseBody(response)).build();}if ("close".quest().header("Connection"))|| "close".equalsIgnoreCase(response.header("Connection"))) {NewStreams();}if ((code == 204 || code == 205) && response.body().contentLength() > 0) {throw new ProtocolException("HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());}return response;}
这个拦截器主要的任务如下:
1.写入请求头
2.写入请求体
3.读取响应头
4.读取响应体
这样所有的Okhttp流程基本分析完毕,再往深入网络连接流这一块能力有限无法进行分析
本文发布于:2024-01-29 02:17:19,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170646584212014.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |