okhttp源码解析流程 仅供参考
目前Android网络开发框架已经离不开okhttp,至少我的所有项目基本上都使用的是okhttp。
所以我们需要知其然,更需要知其所以然,今天就开始进行okhttp的源码进行解析。
我使用的library的版本是3.12.1版本的
implementation 'com.squareup.okhttp3:okhttp:3.12.1'
下面是一个正常的使用
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url("https://www.baidu.com").build();
client.newCall(request).enqueue(new Callback() { // 异步请求
@Override
public void onFailure(Call call, IOException e) {
// 请求失败
}
@Override
public void onResponse(Call call, Response response) throws IOException {
// 请求之后的结果
}
});
try { // 同步请求
Response execute = client.newCall(request).execute(); // 等待结果的返回
} catch (IOException e) {
e.printStackTrace();
}
可以看到上面的两个不同方式的请求数据信息都是会通过newCall来创建一个新的Call对象然后去执行请求。
那么接下来我们一起去看看newCall中做了什么操作
@Override public Call newCall(Request request) { // 调用到了RealCall类中
return RealCall.newRealCall(this, request, false /* for web socket */);
}
在RealCall中创建了一个RealCall对象,并创建事件的监听,将本对象进行返回
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;
}
然后就去执行同步execute和异步enqueue操作。上面既然返回的是Call子类RealCall那么就会在RealCall中进行执行,所以我们找到RealCall类中的方法。我们先看异步enqueue是怎么走的。
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));
}
new AsyncCall(responseCallback) 创建一个新的AsyncCall()异步请求
Dispatcher # enqueue()
void enqueue(AsyncCall call) {
synchronized (this) { // 防止重复添加,所以添加锁
readyAsyncCalls.add(call); // 将当前请求call放到准备异步请求的队列中
}
promoteAndExecute();
}
Dispatcher # promoteAndExecute()
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));
List<AsyncCall> executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
// 对当前队列里面的数据请求进行遍历
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
// private int maxRequests = 64;
// 如果当前正在运行异步的请求数量如果大于等于64 暂时不管
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
// private int maxRequestsPerHost = 5;
// 如果当前运行请求的当前Host大于等于5,即表示当前Host的连接暂时不做处理
if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.
i.remove();
executableCalls.add(asyncCall); // 将请求添加到list集合中
runningAsyncCalls.add(asyncCall); // 将请求添加到运行异步请求队列中
}
isRunning = runningCallsCount() > 0; // 是否正在运行
}
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
asyncCall.executeOn(executorService()); // 获取线程池
}
return isRunning;
}
Dispatcher # executorService() 获取线程池对象
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;
}
AsyncCall # executeOn()
void executeOn(ExecutorService executorService) {
assert (!Thread.holdsLock(client.dispatcher()));
boolean success = false;
try {
executorService.execute(this); // 线程池开始进行当前Call 传入为this
success = true;
} catch (RejectedExecutionException e) {
InterruptedIOException ioException = new InterruptedIOException("executor rejected");
ioException.initCause(e);
eventListener.callFailed(RealCall.this, ioException);
responseCallback.onFailure(RealCall.this, ioException);
} finally {
if (!success) {
client.dispatcher().finished(this); // This call is no longer running!
}
}
}
由于上面传入的是AsyncCall的执行方法,这里的this指的是AsyncCall对象,所以执行的是AsyncCall # execute()方法。
@Override protected void execute() {
boolean signalledCallback = false;
timeout.enter();
try {
// 获取用户设置响应拦截器链 TODO 很重要
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) { // 重试拦截是否被取消
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
e = timeoutExit(e);
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
大概时序图如下
下面我们开始查看getResponseWithInterceptorChain();源代码流程。
RealCall # getResponseWithInterceptorChain()
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors()); // 首先添加客户端所有的拦截器
interceptors.add(retryAndFollowUpInterceptor); // 添加重试并跟进拦截器
interceptors.add(new BridgeInterceptor(client.cookieJar())); // 添加桥接拦截器
interceptors.add(new CacheInterceptor(client.internalCache())); // 添加缓存拦截器
interceptors.add(new ConnectInterceptor(client)); // 添加连接拦截器
if (!forWebSocket) { // 初始化RealCall的时候是否设置 默认false 自动添加 true则不添加网络拦截器
interceptors.addAll(client.networkInterceptors()); // 添加网络拦截器
}
interceptors.add(new CallServerInterceptor(forWebSocket)); // 最后请求服务拦截器
// 创建拦截器链
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
// 链开始执行
return chain.proceed(originalRequest);
}
}
chain.proceed(originalRequest); // 调用到Interceptor.Chain接口中,由于实现是RealInterceptorChain类进行实现的,所以我们直接看这个方法里面的proceed()方法付调用过程。
RealInterceptorChain # proceed()
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
proceed()方法重载,依旧还是调用RealInterceptorChain # proceed()方法,只是参数变多了。
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
// 当前处理的拦截器已经大于设置的拦截器数量,抛出资源异常的Error
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// If we already have a stream, confirm that the incoming request will use it.
if (this.httpCodec != null && !this.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.httpCodec != 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, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
// 因为是最开始的时候 这个index为0,所以这里拿取第一个拦截器,上面的代码将下一个拦截器的index+1
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
if (httpCodec != 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;
}
interceptor.intercept(next); 将拿取到的拦截器去执行拦截接口
Response intercept(Chain chain) throws IOException;
看在什么地方实现。
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new TokenHeaderInterceptor())
.addInterceptor(new NullResponseInterceptor()) // 返回空字符的时候
.connectTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
.build();
按照添加拦截器的顺序会先执行token,下面的动态设置Token并不是只有实现拦截器接口才可以实现,亦可以在请求数据的时候addHeader实现。
public class TokenHeaderInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
String token = SPWatchGetUtil.getInstance().getToken();
if (TextUtils.isEmpty(token)){
Request originalRequest = chain.request();
return chain.proceed(originalRequest);
} else {
Request originalRequest = chain.request();
Request updateRequest = originalRequest
.newBuilder()
.header("Authorization",token)
.build();
return chain.proceed(updateRequest); // 上面操作完成之后 开始执行下一个拦截器信息
}
}
}
下面又开始执行Interceptor.Chain接口中的proceed方法;
RealInterceptorChain # proceed()
方法重载
RealInterceptorChain # proceed()
执行intercept()拦截器拦截方法。 第二个是NullResponseInterceptor,服务器返回null时的处理。
// 由于这个方法是已经响应之后的数据进行更改,所以我们需要先去请求,拿到结果在进行分析
public class NullResponseInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request()); // 先调用链式请求
// LogUtils.e("NullResponseInterceptor",response.body().string()); // 不要在这里去打印拿取数据,response.body().string()只能获取一次
// 拿到数据进行解析
if (response.body().contentLength() == 0 && response.code() == 200) {
MediaType mediaType = response.body().contentType();
//*********添加自定义的解析基类*********
BaseJson baseJson = new BaseJson();
baseJson.setCode(200);
baseJson.setMessage("Success");
baseJson.setData("成功");
//*********添加自定义的解析基类*********
String baseJsonString = new Gson().toJson(baseJson);
ResponseBody responseBody = ResponseBody.create(mediaType, baseJsonString);
return response.newBuilder().body(responseBody).build();
} else {
return response;
}
}
}
跟上面的逻辑一样,接着会进入到下一个拦截器的intercept的方法。
上面我们自定义的inteceptor已经走完了,那么接下来是okhttp自带的(重定向,桥接,缓存,连接,网络,请求服务),先看看重定向里面做了什么操作吧。
RetryAndFollowUpInterceptor # intercept()
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request(); // 获取请求
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Call call = realChain.call(); // 获取Call对象
EventListener eventListener = realChain.eventListener(); // 获取事件的监听
// 创建请求对应的数据流
StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(request.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
int followUpCount = 0; // 重定向次数
Response priorResponse = null;
while (true) {
if (canceled) { // 是否取消当前请求
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.
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 {
// 计算接收到用户响应时要发出的HTTP请求。这将添加身份验证头、跟踪重定向或处理客户端请求超时。如果后续操作不必要或不适用,则返回null。
followUp = followUpRequest(response, streamAllocation.route());
} catch (IOException e) {
streamAllocation.release();
throw e;
}
if (followUp == null) {
streamAllocation.release();
return response; // 只有这里有正常返回
}
// 关闭closeable,忽略所有选中的异常。如果closeable为null,则不执行任何操作。
closeQuietly(response.body());
// private static final int MAX_FOLLOW_UPS = 20; 最大尝试次数 20
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
// 请求体为不重复请求体时,报出异常
if (followUp.body() instanceof UnrepeatableRequestBody) {
streamAllocation.release();
throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
}
// 如果后续HTTP请求可以重用此引擎使用的连接,则返回true
if (!sameConnection(response, followUp.url())) {
streamAllocation.release();
streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(followUp.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
} else if (streamAllocation.codec() != null) {
throw new IllegalStateException("Closing the body of " + response
+ " didn't close its backing stream. Bad interceptor?");
}
request = followUp;
priorResponse = response;
}
}
从上面的代码可以看出,重定向创建了请求对应的数据流对象,并将数据流对象传入到下一个拦截器中,并做了异常的处理,路由器异常和连接不到服务器异常的处理,如果以上两种异常都不是,那么释放数据流对象资源。但是在出现异常的时候有recover方法的判断,是否进行重定向。
private boolean recover(IOException e, StreamAllocation streamAllocation,
boolean requestSendStarted, Request userRequest) {
streamAllocation.streamFailed(e);
// 用户是否设置不进行重定向,如果不能重定向,即不进行重试
// The application layer has forbidden retries.
if (!client.retryOnConnectionFailure()) return false;
// 如果请求已经发送了 并且 请求体是不重复类型的请求体, 不进行重试
// We can't send the request body again.
if (requestSendStarted && userRequest.body() instanceof UnrepeatableRequestBody) return false;
// 对异常的相关信息的处理,是否需要进行重试
if (!isRecoverable(e, requestSendStarted)) return false;
// 没有更多的路由器进行重试(例如一个域名可能存在多个ip地址,可进行再次请求),
// No more routes to attempt.
if (!streamAllocation.hasMoreRoutes()) return false;
// For failure recovery, use the same route selector with a new connection.
return true;
}
isRecoverable(e, requestSendStarted)
private boolean isRecoverable(IOException e, boolean requestSendStarted) {
// 如果是网络协议的异常,不进行重试
if (e instanceof ProtocolException) {
return false;
}
// 如果这个异常是一个拦截器的异常,并且请求超时且请求没有发送,进行重试
// If there was an interruption don't recover, but if there was a timeout connecting to a route
// we should try the next route (if there is one).
if (e instanceof InterruptedIOException) {
return e instanceof SocketTimeoutException && !requestSendStarted;
}
// SSL协议握手异常并且证书异常的情况不进行重试
// Look for known client-side or negotiation errors that are unlikely to be fixed by trying
// again with a different route.
if (e instanceof SSLHandshakeException) {
// If the problem was a CertificateException from the X509TrustManager,
// do not retry.
if (e.getCause() instanceof CertificateException) {
return false;
}
}
// SSL对等未验证异常 不进行重试
if (e instanceof SSLPeerUnverifiedException) {
// e.g. a certificate pinning error.
return false;
}
return true;
}
然后进行查看下一个拦截器桥接,看看具体做了什么操作。
BridgeInterceptor # intercept()
@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
// 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());
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
Response.Builder responseBuilder = networkResponse.newBuilder()
.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();
}
由上面的代码我们可以知道这个拦截器的主要作用的设置请求相关的请求头信息(内容类型,请求体长度,连接方式,请求头,Cookie,HTTP客户端运行的浏览器类型的详细信息,接收编码是否是gzip),等拦截器拿到请求之后的响应做了接收编码是GZip的相关处理。
接下来是缓存拦截器。
CacheInterceptor # intercept()
@Override public Response intercept(Chain chain) throws IOException {
Response cacheCandidate = cache != null
? cache.get(chain.request()) // 1. 获取当前请求缓存
: null;
long now = System.currentTimeMillis();
// 2. 创建缓存策略工厂,并获取当前请求的缓存策略
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest; // 拿到当前请求的网络请求缓存
Response cacheResponse = strategy.cacheResponse; // 拿到当前请求的网络响应缓存
if (cache != null) { // 拿到的当前请求缓存不为空的情况
cache.trackResponse(strategy); // 请求数量进行+1处理
}
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) // http协议版本1.1
.code(504) // 默认code
.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) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
Response networkResponse = null;
try {
networkResponse = chain.proceed(networkRequest); // 拿到本次请求的响应数据
} finally {
// 如果我们在I/O或其他方面崩溃,释放缓存体。
// 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) { // code 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());
}
}
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build(); // 将请求之后的数据信息进行保存
if (cache != null) { // 之前获取缓存的时候不为空的情况
// method 是否是 HEAD 并且 如果可以存储响应以稍后服务另一个请求,则返回true。
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
// Offer this request to the cache.
CacheRequest cacheRequest = cache.put(response);
return cacheWritingResponse(cacheRequest, response); // 进行缓存
}
// method.equals("POST") method.equals("PATCH") method.equals("PUT") method.equals("DELETE") method.equals("MOVE");
if (HttpMethod.invalidatesCache(networkRequest.method())) {
try {
cache.remove(networkRequest); // 将缓存进行移除,这些方法只能做一次请求
} catch (IOException ignored) {
// The cache cannot be written.
}
}
}
return response;
}
- 获取当前请求缓存 cache.get(chain.request()) 调用到Cache类中执行get方法
Cahe # get()
@Nullable Response get(Request request) {
String key = key(request.url()); // 以url作为key值 进行MD5加密 转为16进制的String key
DiskLruCache.Snapshot snapshot; // 磁盘缓存快照
Entry entry;
try {
snapshot = cache.get(key); // 返回名为key的条目的快照,如果该条目不存在,则返回null,当前不可读。如果返回一个值,它将被移动到LRU队列的头部
if (snapshot == null) {
return null;
}
} catch (IOException e) {
// Give up because the cache cannot be read.
return null;
}
try {
entry = new Entry(snapshot.getSource(ENTRY_METADATA)); // 如果上面拿到的快照不为空,则获取第一个数据并保存到Entry
} catch (IOException e) {
Util.closeQuietly(snapshot);
return null;
}
Response response = entry.response(snapshot);
if (!entry.matches(request, response)) { // 判断响应与请求是否是匹配的
Util.closeQuietly(response.body());
return null;
}
return response;
}
- 创建缓存策略工厂,并获取当前请求的缓存策略
创建缓存策略工厂,假设我们当前请求有进行设备缓存 client.cache(); 并且符合上面Cache.get()方法里面的条件,即cacheResponse不为空。
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 (int i = 0, size = headers.size(); i < size; i++) {
String fieldName = headers.name(i);
String value = headers.value(i);
// Date头域表示消息发送的时间,服务器响应中要包含这个头部,因为缓存在评估响应的新鲜度时要用到,其时间的描述格式由RFC822定义。
if ("Date".equalsIgnoreCase(fieldName)) {
servedDate = HttpDate.parse(value);
servedDateString = value;
// WEB服务器表明该实体将在什么时候过期,对于过期了的对象,只有在跟WEB服务器验证了其有效性后,才能用来响应客户请求。是 HTTP/1.0 的头部
} else if ("Expires".equalsIgnoreCase(fieldName)) {
expires = HttpDate.parse(value);
// WEB服务器认为对象的最后修改时间,比如文件的最后修改时间,动态页面的最后产生时间等等。
} else if ("Last-Modified".equalsIgnoreCase(fieldName)) {
lastModified = HttpDate.parse(value);
lastModifiedString = value;
// 就是一个对象(比如URL)的标志值,就一个对象而言,比如一个html文件,如果被修改了,其Etag也会别修改,所以,ETag的作用跟Last-Modified的作用差不多,主要供WEB服务器判断一个对象是否改变了。
} else if ("ETag".equalsIgnoreCase(fieldName)) {
etag = value;
// 当代理服务器用自己缓存的实体去响应请求时,用该头部表明该实体从产生到现在经过多长时间了。
} else if ("Age".equalsIgnoreCase(fieldName)) {
ageSeconds = HttpHeaders.parseSeconds(value, -1);
}
}
}
}
如果当前的请求信息之前是有缓存的,并且缓存与请求相对应。策略工厂会设置请求头相关配置。
CacheInterceptor 主要是对缓存信息的相关处理。在执行下一个拦截器之前先进行缓存信息的获取和是否可使用网络请求,两者都不行就进行异常返回code 504。拿到响应信息之后进行相关的缓存处理,如果返回code为304,拿到数据之后进行缓存数据并返回,如果cache不为空就(1.method为Head并且能给一下个请求使用,进行缓存 2. 对PUT,DELETE等操作进行移除缓存)。
ConnectInterceptor # intercept()
@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);
}
ConnectInterceptor 主要做了HttpCodec对象的创建和RealConnect对象的创建,并将创建后的对象传递给下一层的拦截器进行调用使用。
CallServerInterceptor # intercept()
@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);
// 请求头结束
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"))) {
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 = responseBuilder
.request(request) // 请求
.handshake(streamAllocation.connection().handshake()) // 握手协议
.sentRequestAtMillis(sentRequestMillis) // 请求时间 毫秒
.receivedResponseAtMillis(System.currentTimeMillis()) // 响应时间毫秒
.build();
int code = response.code(); // 响应码
if (code == 100) { // 如果服务器返回100 表示需要在进行一次请求
// 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) {
// 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();
}
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
return response;
}
CallServerInterceptor主要是去请求服务器,先进行请求头的请求,请求结束,发现没有其他异常情况,开始请求体的请求,握手协议等,请求结束对正常响应码做处理,例如100表示需要继续进行请求。
请求结束之后,如果客户端与服务端的连接方式非长连接,需要断开与服务器的连接。
如果响应码是204或者205表示没有任何内容的返回,但也属于正常的请求状态。
后续时序图,只到了桥接拦截器,后面大同小异,未做。
大概流程图如下
由上可总结出OkHttp所使用的设计模式:
1. 单例模式:(建议用点力模式创建okHttpClient)okHttpClient,可以通过new OkHttpClient() 或 new OkHttpClient.builder()来创建对象,但是特别注意,OkHttpClient() 对象最好是共享的,建议使用单例模式创建。因为每个OkHttpClient对象都管理自己独有的线程池和连接池。
2. 外观模式:OkHttpClient里面组合了很多的类对象。其实是将OkHttp的很多功能模块全部包装进这个类中,让这个类单独提供对外的api,这种设计模式叫外观模式。(外观模式:隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口)。
3. 建造者模式:OkHttpClient比较复杂,属性较多,而且客户的组合需求多样化,所以OkHttp使用建造者模式(build模式:使用多个简单的对象,一步一步构建成一个复杂的对象,一个Builder类会一步一步构造最终的对象)。
4. 工厂方法模式:Call接口提供了内部接口Factory用于将对象的创建延迟到该工厂类的子类中进行,从而实现动态的配置,工厂方法模式。(工厂方法模式:这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且通过使用一个共同的接口来指向新创建的对象。)
5. 享元模式:在Dispatcher的线程池中,所用到了享元模式,一个不限容量的线程池,线程空闲时存活时间为60秒。线程池实现了对象复用,降低线程创建销毁开销,从设计模式来讲使用了享元模式。(享元模式:尝试重用现有的同类对象,如果未找到匹配对象,则创建新对象,主要用于减少创建对象的数量,以减少内存占用和提高性能)。
6. 责任链模式:在OkHttp中的拦截器模块,执行过程用到。OkHttp3的拦截器链中,内置了5个默认的拦截器,分别用于重试、桥接、缓存、链接、网络读写(责任链模式:为请求创建了一个接收者对象的链,这种模式给予请求类型,对请求的发送者和接受者进行解藕。这种类型的设计模式属于行为型模式。在这种模式中,通常每个接受者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,以此类推)。
7. 策略模式:CacheInterceptor实现了数据的选择策略,来自网络还是来自本地?这个场景也是比较契合策略模式场景,CacheInterceptor需要一个策略提供者提供它一个策略,CacheInterceptor根据这个策略去选择走网络数据还是本地缓存。
缓存的策略过程:
1. 请求头包含“If-Modified- Since”或“If-None-Macth”暂时不走缓存
2. 客户端通过cacheControl指定了无缓存,不走缓存
3. 客户端通过cacheControl指定了缓存,则看缓存过期时间,符合要求走缓存。
4. 如果走了网络请求,响应状态码为304(只有客户端请求头包含“If-Modified- Since”或“If-None-Macth”,服务器数据没变化的话会返回304状态码,不会返回响应内容),表示客户端继续用缓存。
(策略模式:一个类的行为或其算法可以在运行时更改,这种类型的设计模式属于行为型模式。策略模式中,我们创建表示各种策略的对象和一个行为随着对象改变的context对象,策略对象改变context对象的执行算法)。