okhttp源码解析--缓存拦截策略
2019-03-14 本文已影响10人
二妹是只猫
Okhttpclient流程图.png
okhttp中设置缓存非常简单,只需要在OkHttpClient中调用cache方法创建
okhttp中设置缓存非常简单,只需要在OkHttpClient中调用cache方法创建
Cache
对象就可以实现:
OkHttpClient okHttpClient = new OkHttpClient().newBuilder().cache(new Cache(new File("cache"),1024*1024*10)).connectTimeout(5,TimeUnit.SECONDS).build();
但具体的实现在源码中还是值得一看的。
Cache的put方法(存):
@Nullable CacheRequest put(Response response) {
String requestMethod = response.request().method();
1 if (HttpMethod.invalidatesCache(response.request().method())) {
try {
remove(response.request());
} catch (IOException ignored) {
// The cache cannot be written.
}
return null;
}
2 if (!requestMethod.equals("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;
}
3 if (HttpHeaders.hasVaryAll(response)) {
return null;
}
4 Entry entry = new Entry(response);
5 DiskLruCache.Editor editor = null;
6 try {
editor = cache.edit(key(response.request().url()));
if (editor == null) {
return null;
}
entry.writeTo(editor);
return new CacheRequestImpl(editor);
} catch (IOException e) {
abortQuietly(editor);
return null;
}
}
- 1.首先判断当前调用请求是否有效,无效就拦截并且缓存中移除
- 2.如果不是GET请求就拦截,这点很好理解POST、DELETE这些方法也没缓存的必要。
- 3.HttpHeaders.hasVaryAll(response)判断请求头是否有效
- 4.使用Entry将我们的请求头包装起来
- 5.创建一个DiskLruCache对象,由此看出okhttp的缓存策略也是LRC
- 6.先将请求url使用md5加密存起来,在调用
entry.writeTo
将请求头、请求方法等存起来,最后返回new CacheRequestImpl(editor)
将请求体存起来。
writTo:
public void writeTo(DiskLruCache.Editor editor) throws IOException {
BufferedSink sink = Okio.buffer(editor.newSink(ENTRY_METADATA));
sink.writeUtf8(url)
.writeByte('\n');
sink.writeUtf8(requestMethod)
.writeByte('\n');
sink.writeDecimalLong(varyHeaders.size())
.writeByte('\n');
for (int i = 0, size = varyHeaders.size(); i < size; i++) {
sink.writeUtf8(varyHeaders.name(i))
.writeUtf8(": ")
.writeUtf8(varyHeaders.value(i))
.writeByte('\n');
}
sink.writeUtf8(new StatusLine(protocol, code, message).toString())
.writeByte('\n');
sink.writeDecimalLong(responseHeaders.size() + 2)
.writeByte('\n');
for (int i = 0, size = responseHeaders.size(); i < size; i++) {
sink.writeUtf8(responseHeaders.name(i))
.writeUtf8(": ")
.writeUtf8(responseHeaders.value(i))
.writeByte('\n');
}
sink.writeUtf8(SENT_MILLIS)
.writeUtf8(": ")
.writeDecimalLong(sentRequestMillis)
.writeByte('\n');
sink.writeUtf8(RECEIVED_MILLIS)
.writeUtf8(": ")
.writeDecimalLong(receivedResponseMillis)
.writeByte('\n');
if (isHttps()) {
sink.writeByte('\n');
sink.writeUtf8(handshake.cipherSuite().javaName())
.writeByte('\n');
writeCertList(sink, handshake.peerCertificates());
writeCertList(sink, handshake.localCertificates());
sink.writeUtf8(handshake.tlsVersion().javaName()).writeByte('\n');
}
sink.close();
}
** CacheRequestImpl:**
CacheRequestImpl(final DiskLruCache.Editor editor) {
this.editor = editor;
this.cacheOut = editor.newSink(ENTRY_BODY);
this.body = new ForwardingSink(cacheOut) {
@Override public void close() throws IOException {
synchronized (Cache.this) {
if (done) {
return;
}
done = true;
writeSuccessCount++;
}
super.close();
editor.commit();
}
};
}
Cache的get方法(取):
@Nullable Response get(Request request) {
String key = key(request.url());
DiskLruCache.Snapshot snapshot;
Entry entry;
try {
1 snapshot = cache.get(key);
if (snapshot == null) {
return null;
}
} catch (IOException e) {
// Give up because the cache cannot be read.
return null;
}
try {
2 entry = new Entry(snapshot.getSource(ENTRY_METADATA));
} catch (IOException e) {
Util.closeQuietly(snapshot);
return null;
}
Response response = entry.response(snapshot);
3 if (!entry.matches(request, response)) {
Util.closeQuietly(response.body());
return null;
}
return response;
}
- 1.获取缓存,如何判断是否为空
- 2.转化成包装类Entry,然后得到Response
- 3.判断请求与响应是否匹配
response():
public Response response(DiskLruCache.Snapshot snapshot) {
String contentType = responseHeaders.get("Content-Type");
String contentLength = responseHeaders.get("Content-Length");
Request cacheRequest = new Request.Builder()
.url(url)
.method(requestMethod, null)
.headers(varyHeaders)
.build();
return new Response.Builder()
.request(cacheRequest)
.protocol(protocol)
.code(code)
.message(message)
.headers(responseHeaders)
.body(new CacheResponseBody(snapshot, contentType, contentLength))
.handshake(handshake)
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(receivedResponseMillis)
.build();
}
}
缓存策略这里出要写的是它的源码,具体的应用是在CacheInterceptor
中,在这篇okhttp源码解析--拦截器有介绍。