CacheInterceptor
2019-10-24 本文已影响0人
大佬的上半生
CacheInterceptor缓存过滤器、
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val cacheCandidate = cache?.get(chain.request())
val now = System.currentTimeMillis()
val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
val networkRequest = strategy.networkRequest
val cacheResponse = strategy.cacheResponse
cache?.trackResponse(strategy)
if (cacheCandidate != null && cacheResponse == null) {
cacheCandidate.body?.closeQuietly()
}
if (networkRequest == null && cacheResponse == null) {
return Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(HTTP_GATEWAY_TIMEOUT)
.message("Unsatisfiable Request (only-if-cached)")
.body(EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build()
}
if (networkRequest == null) {
return cacheResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build()
}
var networkResponse: Response? = null
try {
networkResponse = chain.proceed(networkRequest)
} finally {
if (networkResponse == null && cacheCandidate != null) {
cacheCandidate.body?.closeQuietly()
}
}
// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) {
if (networkResponse?.code == HTTP_NOT_MODIFIED) {
val 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 {
cacheResponse.body?.closeQuietly()
}
}
val response = networkResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build()
if (cache != null) {
if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) {
// Offer this request to the cache.
val cacheRequest = cache.put(response)
return cacheWritingResponse(cacheRequest, response)
}
if (HttpMethod.invalidatesCache(networkRequest.method)) {
try {
cache.remove(networkRequest)
} catch (_: IOException) {
// The cache cannot be written.
}
}
}
return response
}
/**
* Returns a new source that writes bytes to [cacheRequest] as they are read by the source
* consumer. This is careful to discard bytes left over when the stream is closed; otherwise we
* may never exhaust the source stream and therefore not complete the cached response.
*/
@Throws(IOException::class)
private fun cacheWritingResponse(cacheRequest: CacheRequest?, response: Response): Response {
// Some apps return a null body; for compatibility we treat that like a null cache request.
if (cacheRequest == null) return response
val cacheBodyUnbuffered = cacheRequest.body()
val source = response.body!!.source()
val cacheBody = cacheBodyUnbuffered.buffer()
val cacheWritingSource = object : Source {
var cacheRequestClosed: Boolean = false
@Throws(IOException::class)
override fun read(sink: Buffer, byteCount: Long): Long {
val bytesRead: Long
try {
bytesRead = source.read(sink, byteCount)
} catch (e: IOException) {
if (!cacheRequestClosed) {
cacheRequestClosed = true
cacheRequest.abort() // Failed to write a complete cache response.
}
throw e
}
if (bytesRead == -1L) {
if (!cacheRequestClosed) {
cacheRequestClosed = true
cacheBody.close() // The cache response is complete!
}
return -1
}
sink.copyTo(cacheBody.buffer, sink.size - bytesRead, bytesRead)
cacheBody.emitCompleteSegments()
return bytesRead
}
override fun timeout(): Timeout {
return source.timeout()
}
@Throws(IOException::class)
override fun close() {
if (!cacheRequestClosed &&
!discard(ExchangeCodec.DISCARD_STREAM_TIMEOUT_MILLIS, MILLISECONDS)) {
cacheRequestClosed = true
cacheRequest.abort()
}
source.close()
}
}
val contentType = response.header("Content-Type")
val contentLength = response.body.contentLength()
return response.newBuilder()
.body(RealResponseBody(contentType, contentLength, cacheWritingSource.buffer()))
.build()
}
companion object {
private fun stripBody(response: Response?): Response? {
return if (response?.body != null) {
response.newBuilder().body(null).build()
} else {
response
}
}
/** Combines cached headers with a network headers as defined by RFC 7234, 4.3.4. */
private fun combine(cachedHeaders: Headers, networkHeaders: Headers): Headers {
val result = Headers.Builder()
for (index in cachedHeaders.names().indices) {
val fieldName = cachedHeaders.name(index)
val value = cachedHeaders.value(index)
if ("Warning".equals(fieldName, ignoreCase = true) && value.startsWith("1")) {
// Drop 100-level freshness warnings.
continue
}
if (isContentSpecificHeader(fieldName) ||
!isEndToEnd(fieldName) ||
networkHeaders[fieldName] == null) {
result.addLenient(fieldName, value)
}
}
for (index in networkHeaders.names().indices) {
val fieldName = networkHeaders.name(index)
if (!isContentSpecificHeader(fieldName) && isEndToEnd(fieldName)) {
result.addLenient(fieldName, networkHeaders.value(index))
}
}
return result.build()
}
/**
* Returns true if [fieldName] is an end-to-end HTTP header, as defined by RFC 2616,
* 13.5.1.
*/
private fun isEndToEnd(fieldName: String): Boolean {
return !"Connection".equals(fieldName, ignoreCase = true) &&
!"Keep-Alive".equals(fieldName, ignoreCase = true) &&
!"Proxy-Authenticate".equals(fieldName, ignoreCase = true) &&
!"Proxy-Authorization".equals(fieldName, ignoreCase = true) &&
!"TE".equals(fieldName, ignoreCase = true) &&
!"Trailers".equals(fieldName, ignoreCase = true) &&
!"Transfer-Encoding".equals(fieldName, ignoreCase = true) &&
!"Upgrade".equals(fieldName, ignoreCase = true)
}
/**
* Returns true if [fieldName] is content specific and therefore should always be used
* from cached headers.
*/
private fun isContentSpecificHeader(fieldName: String): Boolean {
return "Content-Length".equals(fieldName, ignoreCase = true) ||
"Content-Encoding".equals(fieldName, ignoreCase = true) ||
"Content-Type".equals(fieldName, ignoreCase = true)
}
}
执行下一个任务之前
val cacheCandidate = cache?.get(chain.request())
val now = System.currentTimeMillis()
//创建缓存器
val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
val networkRequest = strategy.networkRequest
//获取缓存
val cacheResponse = strategy.cacheResponse
cache?.trackResponse(strategy)
if (cacheCandidate != null && cacheResponse == null) {
//缓存为空关闭cacheCandidate
cacheCandidate.body?.closeQuietly()
}
//没有网络&&h缓存为空构建一个新的Response 状态码为504
return Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(HTTP_GATEWAY_TIMEOUT)
.message("Unsatisfiable Request (only-if-cached)")
.body(EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build()
}
//如果没有网络,读取缓存
if (networkRequest == null && cacheResponse == null) {
if (networkRequest == null) {
return cacheResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build()
}
var networkResponse: Response? = null
执行下一个 networkResponse = chain.proceed(networkRequest)
执行下一个任务之后
finally {
// 如果我们在I / O或其他方面崩溃,关闭缓存资源
if (networkResponse == null && cacheCandidate != null) {
cacheCandidate.body?.closeQuietly()
}
}
//如果有缓存,状态为304则获取缓存
if (cacheResponse != null) {
if (networkResponse?.code == HTTP_NOT_MODIFIED) {
val 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()
//在合并标头之后但在剥离之前更新缓存
// Content-Encoding标头(由initContentStream()执行)。
cache!!.trackConditionalCacheHit()
cache.update(cacheResponse, response)
return response
} else {
cacheResponse.body?.closeQuietly()
}
}
//读取网络
val response = networkResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build()
if (cache != null) {
if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) {
// 将响应进项缓存
val cacheRequest = cache.put(response)
return cacheWritingResponse(cacheRequest, response)
}
//删除无效缓存
if (HttpMethod.invalidatesCache(networkRequest.method)) {
try {
cache.remove(networkRequest)
} catch (_: IOException) {
// The cache cannot be written.
}
}
}
//返回结果
return response
}
上面基本流程就是
1.读取候选缓存;
2.创建缓存策略(根据头信息,判断强制缓存,对比缓存等策略);
3.根据策略,不使用网络,缓存又没有直接报错;
4.根据策略,不使用网络,有缓存就直接返回;
5.前面个都没有返回,读取网络结果(跑下一个拦截器);
6.接收到的网络结果,如果是code 304, 使用缓存,返回缓存结果(对比缓存)
7.读取网络结果;
8.对数据进行缓存;删除无效缓存;
9.返回网络读取的结果。