Browse Source

优化缓存相关代码
响应可以判断是否来自于缓存

drake 2 years ago
parent
commit
6bdcbfd6a7

+ 1 - 0
docs/cache.md

@@ -51,6 +51,7 @@ scopeNetLife {
 | READ_THEN_REQUEST | 先从缓存读取,如果失败再从网络读取, 强制写入缓存 |
 | REQUEST_THEN_READ | 先从网络读取,如果失败再从缓存读取, 强制写入缓存 |
 
+> 在okHttp中`response.cacheResponse`不为null的时候即代表response来自于本地缓存, 强制缓存或Http缓存协议都如此
 
 ## 缓存+网络
 

+ 67 - 9
net/src/main/java/com/drake/net/cache/ForceCache.kt

@@ -23,6 +23,9 @@ import okhttp3.internal.EMPTY_HEADERS
 import okhttp3.internal.cache.CacheRequest
 import okhttp3.internal.cache.DiskLruCache
 import okhttp3.internal.closeQuietly
+import okhttp3.internal.discard
+import okhttp3.internal.http.ExchangeCodec
+import okhttp3.internal.http.RealResponseBody
 import okhttp3.internal.http.StatusLine
 import okhttp3.internal.platform.Platform
 import okhttp3.internal.toLongOrDefault
@@ -39,6 +42,7 @@ import java.security.cert.CertificateEncodingException
 import java.security.cert.CertificateException
 import java.security.cert.CertificateFactory
 import java.util.*
+import java.util.concurrent.TimeUnit
 
 /**
  * Caches HTTP and HTTPS responses to the filesystem so they may be reused, saving time and
@@ -158,17 +162,69 @@ class ForceCache internal constructor(
         return entry.response(snapshot, request.body)
     }
 
-    internal fun put(response: Response): CacheRequest? {
+    internal fun put(response: Response): Response {
+        if (!response.isSuccessful) return response
         val entry = Entry(response)
         var editor: DiskLruCache.Editor? = null
-        try {
-            editor = cache.edit(key(response.request)) ?: return null
+        val cacheRequest: CacheRequest? = try {
+            editor = cache.edit(key(response.request)) ?: return response
             entry.writeTo(editor)
-            return RealCacheRequest(editor)
+            RealCacheRequest(editor)
         } catch (_: IOException) {
             abortQuietly(editor)
-            return null
+            null
+        }
+        // Some apps return a null body; for compatibility we treat that like a null cache request.
+        cacheRequest ?: return response
+        val cacheBody = cacheRequest.body().buffer()
+        val responseBody = response.body ?: return response
+        val source = responseBody.source()
+        val cacheWritingSource = object : Source {
+            private var cacheRequestClosed = 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() = source.timeout()
+
+            @Throws(IOException::class)
+            override fun close() {
+                if (!cacheRequestClosed &&
+                    !discard(ExchangeCodec.DISCARD_STREAM_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) {
+                    cacheRequestClosed = true
+                    cacheRequest.abort()
+                }
+                source.close()
+            }
         }
+        val contentType = response.header("Content-Type")
+        val contentLength = responseBody.contentLength()
+        return response.newBuilder()
+            .body(RealResponseBody(contentType, contentLength, cacheWritingSource.buffer()))
+            .build()
     }
 
     @Throws(IOException::class)
@@ -549,8 +605,8 @@ class ForceCache internal constructor(
         private fun writeCertList(sink: BufferedSink, certificates: List<Certificate>) {
             try {
                 sink.writeDecimalLong(certificates.size.toLong()).writeByte('\n'.toInt())
-                for (i in 0 until certificates.size) {
-                    val bytes = certificates[i].encoded
+                for (element in certificates) {
+                    val bytes = element.encoded
                     val line = bytes.toByteString().base64()
                     sink.writeUtf8(line).writeByte('\n'.toInt())
                 }
@@ -571,16 +627,18 @@ class ForceCache internal constructor(
                 .method(requestMethod, requestBody)
                 .headers(varyHeaders)
                 .build()
-            return Response.Builder()
+            val builder = Response.Builder()
                 .request(cacheRequest)
                 .protocol(protocol)
                 .code(code)
                 .message(message)
                 .headers(responseHeaders)
-                .body(CacheResponseBody(snapshot, contentType, contentLength))
                 .handshake(handshake)
                 .sentRequestAtMillis(sentRequestMillis)
                 .receivedResponseAtMillis(receivedResponseMillis)
+            return builder
+                .cacheResponse(builder.build())
+                .body(CacheResponseBody(snapshot, contentType, contentLength))
                 .build()
         }
 

+ 3 - 71
net/src/main/java/com/drake/net/interceptor/NetOkHttpInterceptor.kt

@@ -14,18 +14,10 @@ import okhttp3.CacheControl
 import okhttp3.Call
 import okhttp3.Interceptor
 import okhttp3.Response
-import okhttp3.internal.discard
-import okhttp3.internal.http.ExchangeCodec
-import okhttp3.internal.http.RealResponseBody
-import okio.Buffer
-import okio.Source
-import okio.buffer
-import java.io.IOException
 import java.lang.ref.WeakReference
 import java.net.ConnectException
 import java.net.SocketTimeoutException
 import java.net.UnknownHostException
-import java.util.concurrent.TimeUnit
 
 /**
  * Net代理OkHttp的拦截器
@@ -49,17 +41,17 @@ object NetOkHttpInterceptor : Interceptor {
                 when (cacheMode) {
                     CacheMode.READ -> cache.get(request) ?: throw NoCacheException(request)
                     CacheMode.READ_THEN_REQUEST -> cache.get(request) ?: chain.proceed(request).run {
-                        cacheWritingResponse(cache, this)
+                        cache.put(this)
                     }
                     CacheMode.REQUEST_THEN_READ -> try {
                         chain.proceed(request).run {
-                            cacheWritingResponse(cache, this)
+                            cache.put(this)
                         }
                     } catch (e: Exception) {
                         cache.get(request) ?: throw NoCacheException(request)
                     }
                     CacheMode.WRITE -> chain.proceed(request).run {
-                        cacheWritingResponse(cache, this)
+                        cache.put(this)
                     }
                     else -> chain.proceed(request)
                 }
@@ -103,64 +95,4 @@ object NetOkHttpInterceptor : Interceptor {
             }
         }
     }
-
-    /** 缓存网络响应 */
-    @Throws(IOException::class)
-    private fun cacheWritingResponse(cache: ForceCache, response: Response): Response {
-        // Some apps return a null body; for compatibility we treat that like a null cache request.
-        if (!response.isSuccessful) return response
-        val cacheRequest = cache.put(response) ?: return response
-        val cacheBodyUnbuffered = cacheRequest.body()
-
-        val source = response.body!!.source()
-        val cacheBody = cacheBodyUnbuffered.buffer()
-
-        val cacheWritingSource = object : Source {
-            private var cacheRequestClosed = 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() = source.timeout()
-
-            @Throws(IOException::class)
-            override fun close() {
-                if (!cacheRequestClosed &&
-                    !discard(ExchangeCodec.DISCARD_STREAM_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) {
-                    cacheRequestClosed = true
-                    cacheRequest.abort()
-                }
-                source.close()
-            }
-        }
-
-        val contentType = response.header("Content-Type")
-        val contentLength = response.body?.contentLength() ?: 0
-        return response.newBuilder()
-            .body(RealResponseBody(contentType, contentLength, cacheWritingSource.buffer()))
-            .build()
-    }
 }