https://juejin.cn/user/2893570335574632/posts
-
减少服务器请求次数 -
减少用户等待时间 -
增加应用流畅度 -
节省用户流量(虽然现在流量也不怎么值钱了) -
……
-
ONLY_NETWORK :只请求网络,不加缓存 -
ONLY_CACHE :只读取缓存(没有缓存抛出异常) -
NETWORK_PUT_CACHE : 先请求网络,再写入缓存 -
READ_CACHE_NETWORK_PUT :先读取缓存,如果缓存失效再请求网络更新缓存 -
NETWORK_PUT_READ_CACHE :先请求网络,网络请求失败使用缓存 (未过期缓存)
缓存Key
缓存的Key值需要是唯一的,直接用 URL 来做缓存的Key值,GET 请求是没问题的,但是POST请求参数是放在Body 里边的,POST 请求就需要读取出来请求参数,再添加到URL里生成新的 URl 来当做 缓存Key。
private fun buildCacheKey(request: Request): String {
val requestBody = request.body ?: return request.url.toString()
val buffer = Buffer()
requestBody.writeTo(buffer)
val contentType = requestBody.contentType()
val charset = contentType?.charset(Charsets.UTF_8) ?: Charsets.UTF_8
if (isProbablyUtf8(buffer)) {
val questParam = buffer.readString(charset)
buffer.close()
if (questParam.isBlank()) return request.url.toString()
val builder = request.url.newBuilder()
kotlin.runCatching {
builder.addQueryParameter("${request.method.lowercase()}param", questParam)
return builder.build().toString()
}.onFailure {
return ""
}
}
return request.url.toString()
}
override fun intercept(chain: Interceptor.Chain): Response {
val initialRequest = chain.request()
val strategy = CacheUtil.getCacheStrategy(initialRequest)
val newRequest = initialRequest.rmCacheHeader()
if (strategy == null) return chain.proceed(newRequest)// 策略为空,直接返回网络结果
// ONLY_NETWORK 直接请求网络
if (strategy.cacheMode == CacheMode.ONLY_NETWORK) return chain.proceed(newRequest)
// ONLY_CACHE 只读取缓存
if (strategy.cacheMode == CacheMode.ONLY_CACHE) {
// 只读缓存模式,缓存为空,返回错误响应
return (if (CacheManager.useExpiredData) mCache.getCache(strategy.cacheKey, newRequest)
else redCache(strategy, newRequest)) ?: Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(HttpURLConnection.HTTP_GATEWAY_TIMEOUT)
.message("no cached data")
.body(EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build()
}
//先取缓存再取网络
if (strategy.cacheMode == CacheMode.READ_CACHE_NETWORK_PUT) {
val cacheResponse = redCache(strategy, newRequest)
if (cacheResponse != null) return cacheResponse
}
try {
// 开始请求网络
val response = chain.proceed(newRequest)
// 成功后写入缓存
if (response.isSuccessful) {
return cacheWritingResponse(mCache.putCache(strategy.cacheKey, response), response)
}
if (strategy.cacheMode == CacheMode.NETWORK_PUT_READ_CACHE) {
return redCache(strategy, newRequest) ?: response
}
return response
} catch (e: Throwable) {
//请求失败尝试读取缓存,缓存没有或者失效,抛异常
if (strategy.cacheMode == CacheMode.NETWORK_PUT_READ_CACHE) {
return redCache(strategy, newRequest) ?: throw e
}
throw e
}
}
/**
* 使用 Header 参数注解
*/
@FormUrlEncoded
@POST("user/login")
suspend fun login(
@Field("username") username: String,
@Field("password") password: String,
@Header(CacheStrategy.CACHE_MODE) cacheMode: String = CacheMode.READ_CACHE_NETWORK_PUT,
@Header(CacheStrategy.CACHE_TIME) cacheTime: String = "10"// 过期时间,10秒 不过期
): BaseResponse<Any>
/**
* 使用 Headers 方法注解
*/
@Headers(
"${CacheStrategy.CACHE_TIME}:-1", // 过期时间,-1 不过期
"${CacheStrategy.CACHE_MODE}:${CacheMode.READ_CACHE_NETWORK_PUT}"
)
@GET("article/list/{page}/json")
suspend fun getPage(@Path("page") page: Any): BaseResponse<Page<ArticleBean>>
internal fun getDiskLruCache(
fileSystem: FileSystem?,
directory: File?,
appVersion: Int,
valueCount: Int,
maxSize: Long
): DiskLruCache {
val cls = DiskLruCache::class.java
return try {
val runnerClass = Class.forName("okhttp3.internal.concurrent.TaskRunner")
val constructor = cls.getConstructor(
FileSystem::class.java,
File::class.java,
Int::class.java,
Int::class.java,
Long::class.java,
runnerClass
)
constructor.newInstance(
fileSystem,
directory,
appVersion,
valueCount,
maxSize,
TaskRunner.INSTANCE
)
} catch (e: Exception) {
try {
val constructor = cls.getConstructor(
FileSystem::class.java,
File::class.java,
Int::class.java,
Int::class.java,
Long::class.java,
Executor::class.java
)
val executor = ThreadPoolExecutor(
0, 1, 60L, TimeUnit.SECONDS,
LinkedBlockingQueue(), threadFactory("OkHttp DiskLruCache", true)
)
constructor.newInstance(
fileSystem,
directory,
appVersion,
valueCount,
maxSize,
executor
)
} catch (e: Exception) {
throw IllegalArgumentException("Please use okhttp 4.0.0 or later")
}
}
}
CacheManager.setCacheModel(CacheMode.READ_CACHE_NETWORK_PUT)// 设置全局缓存模式
.setCacheTime(15 * 1000) // 设置全局 过期时间 (毫秒)
.useExpiredData(true)// 缓存过期时是否继续使用,仅对 ONLY_CACHE 生效
具体使用方式:详见Demo NetCache https://github.com/AleynP/net-cache
版权申明:内容来源网络,版权归原创者所有。除非无法确认,都会标明作者及出处,如有侵权,烦请告知,我们会立即删除并致歉!
原文始发于微信公众号(互联网程序员):结合Retrofit改造OkHttp缓存
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/24001.html