转载请注明出处:/post/724417…
本文出自 容华谢后的博客
0.写在前面
最近要对接口做一些优化,于是就想着给一些频频获取数据的接口加上缓存功用,网上搜上一搜,一般都只支撑GET恳求,可是因为服务器那儿接口比较特殊,参数较多的获取数据接口都是用的POST,用原生的缓存办法还不行。
那只能自己完成一个,支撑GET、POST恳求办法,为了安全还要支撑缓存数据加密,放到项目里试了试,还算比较稳定,于是便有了此篇文章。
1.流程
先看下全体的流程,仍是经过OkHttp的阻拦器完成的,阻拦到客户端的恳求,假如没有缓存,就去服务器恳求数据,然后缓存到本地,然后加密。
假如有缓存,就判别下缓存的时刻,没过期就回来给客户端缓存数据,过期了就再去服务器取一份,重复上面的步骤。
2.完成
完成一个简略的接口恳求,拜访百度页面,然后测试下缓存的作用:
val retrofit = Retrofit.Builder()
.baseUrl("https://api.github.com/")
.client(getOkHttpClient())
.build()
binding.btnRequest.setOnClickListener {
val service = retrofit.create(RetrofitService::class.java)
val call = service.request("https://www.baidu.com")
call.enqueue(object : Callback<ResponseBody> {
override fun onResponse(
call: Call<ResponseBody>,
response: Response<ResponseBody>
) {
val result = response.body()?.string() ?: ""
binding.tvResult.text = result
Log.i("http回来:", result)
}
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
}
})
}
主要看下getOkHttpClient()办法:
/**
* 获取OkHttpClient
*
* @return OkHttpClient
*/
private fun getOkHttpClient(): OkHttpClient {
// 定制OkHttp
val httpClientBuilder = OkHttpClient.Builder()
// 增加呼应数据缓存阻拦器
httpClientBuilder.addInterceptor(CacheInterceptor(this, "key"))
return httpClientBuilder.build()
}
/**
* 缓存数据阻拦器
*
* @param mContext Context
* @param key 秘钥
*/
private class CacheInterceptor(
private val mContext: Context,
private val key: String
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
val request = chain.request()
val cacheKey = HttpUtils.getCacheKey(request)
val cacheFile = File(HttpUtils.getCacheFile(mContext), cacheKey)
// 缓存时刻1小时
val cacheTime = 3600000L
val cacheEnable = (System.currentTimeMillis() - cacheFile.lastModified()) < cacheTime
if (cacheEnable && cacheFile.exists() && cacheFile.length() > 0) {
Log.i(
"CacheInterceptor",
"[intercept] 缓存模式 url:${HttpUtils.getRequestUrl(request)} " +
"过期时刻:${HttpUtils.dateTimeToString(cacheFile.lastModified() + cacheTime)}"
)
val cache = SecurityUtils.decryptContent(cacheFile.readText(), key)
if (cache.isNotEmpty() && cache.startsWith("{") && cache.endsWith("}")) {
return okhttp3.Response.Builder()
.code(200)
.body(cache.toResponseBody())
.request(request)
.message("from disk cache")
.protocol(Protocol.HTTP_2)
.build()
}
}
val response = chain.proceed(request)
val responseBody = response.body ?: return response
val data = responseBody.bytes()
val dataString = String(data)
// 写入缓存
if (response.code == 200) {
// Json数据写入缓存
cacheFile.writeText(SecurityUtils.encryptContent(dataString, key))
} else {
cacheFile.writeText("")
}
return response.newBuilder()
.body(data.toResponseBody(responseBody.contentType()))
.build()
}
}
代码不是许多,加密的逻辑放在SecurityUtils东西类中了,文章末尾下载源码就能够。
3.注意
这个key是秘钥,能够自己自定义,获取这个秘钥的办法,能够写在so里,也能够写成字符数组的办法,经过某种组合获取到,不要固定一个字符串就行,这样比较安全:
// 增加呼应数据缓存阻拦器
httpClientBuilder.addInterceptor(CacheInterceptor(this, "key"))
在写入缓存这儿,判别了code是200就写入缓存,实际业务中,可能有自己的定义办法,code回来200只能证明接口通了,服务器的逻辑有自己的规矩,比如在回来数据中也有一个code标记,能够在if判别里再加一个业务的判别,只要业务回来了成功,再写入缓存。
// 写入缓存
if (response.code == 200) {
// Json数据写入缓存
cacheFile.writeText(SecurityUtils.encryptContent(dataString, key))
} else {
cacheFile.writeText("")
}
4.原生GET恳求缓存
有的同学只想用原生的办法去缓存GET恳求,在此附上代码:
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
// GET恳求
if ("GET" == request.method) {
return if (checkNetwork(mContext)) {
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_NETWORK)
.build()
val response = chain.proceed(request)
response.newBuilder()
.header("Cache-Control", "public, max-age=0")
.removeHeader("Pragma")
.build()
} else {
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build()
val response = chain.proceed(request)
return response.newBuilder()
.header("Cache-Control", "public, only-if-cached, max-stale=604800")
.removeHeader("Pragma")
.build()
}
}
// POST恳求
return chain.proceed(request)
}
5.写在最后
GitHub地址:github.com/alidili/Dem…
到这儿,Retrofit的缓存功用就介绍完了,如有问题能够给我留言评论或许在GitHub中提交Issues,谢谢!