1.拦截器实现拦截,缓存拦截器实现,顺带了解下磁盘缓存(从源码可知默认只缓存GET请求的数据)
默认缓存设置的入口
okHttpClient.newCall(request).execute()
final class RealCall implements Call {
@Override
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
try {//把请求塞入队列,在Dispatcher源码解析中有讲
client.dispatcher().executed(this);
//通过拦截器链获取Response
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally {//通过dispatcher把当前请求从队列中移除
client.dispatcher().finished(this);
}
}
//通过拦截器链获取Response
Response getResponseWithInterceptorChain() throws IOException {
List<Interceptor> interceptors = new ArrayList<>();//所有拦截器都会放到这里
interceptors.addAll(client.interceptors());//开发传入的拦截器
interceptors.add(retryAndFollowUpInterceptor);//重试拦截器
//把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//磁盘缓存调用就是从这个CacheInterceptor开始
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, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
}
/**
* RealInterceptorChain用来包装拦截器,RealInterceptorChain调用proceed()方法取出一个拦截器,执行拦截器的intercept方法,
* 执行完之后取出下一个拦截器,包装成一个新的RealInterceptorChain对象调用proceed()...直到所有拦截器全部执行完
*/
public final class RealInterceptorChain implements Interceptor.Chain {
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) 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.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");
}
//取出下一个拦截器,包装成新的RealInterceptorChain对象
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);//取出当前拦截器
Response response = interceptor.intercept(next);//执行当前拦截器的intercept方法
// 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");
}
return response;
}
}
/**缓存拦截器
* 缓存的操作,读取、更新、删除,真正的缓存实现在 okhttp3.Cache.java
*/
public final class CacheInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Response cacheCandidate = cache != null
? cache.get(chain.request()) //根据request.url获取缓存
: null;
long now = System.currentTimeMillis();
//缓存策略,解析request的header和缓存response的header,获取header中配置的缓存策略信息封装在strategy对象中
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;//解析后更新request的header,生成的新的request对象
Response cacheResponse = strategy.cacheResponse;//解析后更新response的header,生成的新的response对象
if (cache != null) {
cache.trackResponse(strategy);//跟踪缓存调用,记录调用次数
}
//根据缓存策略得到的缓存为空,说明缓存已失效,关闭从缓存读取的response
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.
//根据解析header缓存策略得到的request、response都为空,说明不允许使用缓存
if (networkRequest == null && cacheResponse == null) {
return new Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(504)
.message("Unsatisfiable Request (only-if-cached)")
.body(Util.EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
}
// If we don't need the network, we're done.
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))//去除cacheResponse的body
.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 (networkResponse.code() == HTTP_NOT_MODIFIED) {
Response response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers(), networkResponse.headers()))
.sentRequestAtMillis(networkResponse.sentRequestAtMillis())
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
networkResponse.body().close();
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
cache.trackConditionalCacheHit();
cache.update(cacheResponse, response);
return response;
} else {
closeQuietly(cacheResponse.body());
}
}
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(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);
}
//删除POST、PATCH、PUT、DELETE、MOVE这些类型请求的缓存(GET请求没有删除诶)
if (HttpMethod.invalidatesCache(networkRequest.method())) {
try {
cache.remove(networkRequest);
} catch (IOException ignored) {
// The cache cannot be written.
}
}
}
return response;
}
}
参考资料:
https://www.jianshu.com/p/9cebbbd0eeab
磁盘缓存:OkHttp缓存控制类okhttp3.Cache.java
真正缓存读写是在okhttp3.internal.cache.DiskLruCache.java
DiskLruCache的实现类似Glide的DiskLruCache实现
public final class Cache implements Closeable, Flushable {
private static final int VERSION = 201105;
private static final int ENTRY_METADATA = 0;
private static final int ENTRY_BODY = 1;
private static final int ENTRY_COUNT = 2;
final InternalCache internalCache = new InternalCache() {//
@Override
public Response get(Request request) throws IOException {
return okhttp3.Cache.this.get(request);
}
@Override
public CacheRequest put(Response response) throws IOException {
return okhttp3.Cache.this.put(response);
}
@Override
public void remove(Request request) throws IOException {
okhttp3.Cache.this.remove(request);
}
@Override
public void update(Response cached, Response network) {
okhttp3.Cache.this.update(cached, network);
}
...
};
final DiskLruCache cache;
...
Cache(File directory, long maxSize, FileSystem fileSystem) {
this.cache = DiskLruCache.create(fileSystem, directory, VERSION, ENTRY_COUNT, maxSize);
}
public static String key(HttpUrl url) {//rul用md5加密后转二进制再用utf8编码作为key
return ByteString.encodeUtf8(url.toString()).md5().hex();
}
@Nullable
Response get(Request request) {//读取缓存
String key = key(request.url());
DiskLruCache.Snapshot snapshot;
okhttp3.Cache.Entry entry;
try {
snapshot = cache.get(key);//调用DiskLruCache.get(key)
if (snapshot == null) {
return null;
}
} catch (IOException e) {// Give up because the cache cannot be read.
return null;
}
...
}
@Nullable
CacheRequest put(Response response) {//写入缓存
String requestMethod = response.request().method();
//不缓存POST、PATCH、PUT、DELETE、MOVE这些类型请求的数据
if (HttpMethod.invalidatesCache(response.request().method())) {
try {
remove(response.request());
} catch (IOException ignored) {
// The cache cannot be written.
}
return null;
}
if (!requestMethod.equals("GET")) {//不缓存非GET请求的数据
// Don't cache non-GET responses. We're technically allowed to cache
// HEAD requests and some POST requests, but the complexity of doing
// so is high and the benefit is low.
return null;
}
if (HttpHeaders.hasVaryAll(response)) {
return null;
}
//下面这块具体缓存的读写,与Glide的DiskLruCache非常像,原理是一样的
okhttp3.Cache.Entry entry = new okhttp3.Cache.Entry(response);
DiskLruCache.Editor editor = null;
try {
editor = cache.edit(key(response.request().url()));
if (editor == null) {
return null;
}
entry.writeTo(editor);
return new okhttp3.Cache.CacheRequestImpl(editor);
} catch (IOException e) {
abortQuietly(editor);
return null;
}
}
void remove(Request request) throws IOException {//删除缓存
cache.remove(key(request.url()));
}
void update(Response cached, Response network) {//更新缓存
okhttp3.Cache.Entry entry = new okhttp3.Cache.Entry(network);
DiskLruCache.Snapshot snapshot = ((okhttp3.Cache.CacheResponseBody) cached.body()).snapshot;
DiskLruCache.Editor editor = null;
try {
editor = snapshot.edit(); // Returns null if snapshot is not current.
if (editor != null) {
entry.writeTo(editor);
editor.commit();
}
} catch (IOException e) {
abortQuietly(editor);
}
}
...
}