RxJava+Retrofit如果想做缓存以前会用RxCache(停止维护). 迁移到Kotlin后,使用Coroutines+Retrofit请求网络想做缓存的话貌似资料还不是不多.
CoroutinesCache 是用来做Coroutines+Retrofit缓存的,这个库Readme有点老,下面记录的是我使用的时候踩坑的笔记.
- 依赖
implementation "com.github.diefferson:CoroutinesCache:0.3.0"
- 使用
private val coroutinesCacheLY = lazy {
CoroutinesCache(
this,
byPassCache = false, // false表示从缓存取,true表示从网络获取
lifecycleOwner = this,
jsonMapper = MyGsonRetrofitMapper()// 库中自带了GsonRetrofitMapper,后面细说
)
}
// 接口
@GET(AUTH_HELP_INFO)
suspend fun authHelpInfo(@QueryMap mData: TreeMap<String, Any>): BResponse<AuthHelpInfo>
// 开始使用缓存请求数据
val result = coroutinesCacheLY.value.asyncCache(
{ mRepo.authHelpInfo(mParam) },// 请求网络接口获取数据
"authHelpInfo",// 缓存对应的key
CachePolicy.TimeCache(20, TimeUnit.MINUTES)// 缓存多长时间
)
MyGsonRetrofitMapper.kt
最初我用GsonRetrofitMapper
类, 结果一直请求网络数据, debug后才发现数据没写进去.
@PublishedApi
internal suspend fun <T> getFromSource(
source: suspend () -> T,
key: String,
isLifecycle: Boolean = false
): T {
// 按照实际API从网络请求获取到数据
val result = source()
// 再使用Realm将该对象格式化成字符串保存起来 .
if (jsonMapper.assertValidJson(result)) {
database.getDatabase().apply {
beginTransaction()
copyToRealmOrUpdate(
Cache(
key,
jsonMapper.toJson(result!!), // jsonMapper就是GsonRetrofitMapper对象, 将对象格式化为字符串保存起来.
Calendar.getInstance().time,
isLifecycle
)
)
commitTransaction()
close()
}
}
return result
}
因为当时用GsonRetrofitMapper.assertValidJson()
对数据是否可用做判断,导致返回false
.然后自定义了MyGsonRetrofitMapper
,重写后解决了我的需求:
class MyGsonRetrofitMapper : JsonMapper {
override fun toJson(src: Any): String {
// 这里是将对象转换为字符串,保存起来.
val response = src as BResponse<*>
return Gson().toJson(response, response.javaClass)
}
@Throws(JsonSyntaxException::class)
override fun <T> fromJson(json: String, retrofitResponse: Type): T {
// 这里是将字符串格式化为对象返回.
val reader = StringReader(json)
val parameterizedType = retrofitResponse as ParameterizedType
val storedJson = Gson().fromJson(reader, parameterizedType) as Any
return storedJson as T
}
// BResponse是我封装的类
override fun <T> assertValidJson(result: T): Boolean {
if (result is BResponse<*>) {
if (result.mException == null) { //如果返回的对象中没有错误,就返回true,然后使用Realm保存该对象.
return true
}
}
return false
}
}
data class BResponse<out T>(
val mException: Exception? = null,
val code: String,
val msg: String,
val data: T? = null
)
下面解释下如何从缓存中取出对象的
@PublishedApi
internal inline fun <reified T : Any> getFromCacheValidateTime(
key: String,
timeCache: CachePolicy.TimeCache
): T? {
// 从缓存中取出对象
val resultDb = database.getDatabase().where(Cache::class.java).equalTo("id", key).findFirst()
return if (resultDb != null) {
if (resultDb.date.isValidCache(timeCache.duration, timeCache.timeUnit)) {
val listType = object : TypeToken<T>() {}.type
// 这里是将取出的字符串格式化为对象然后返回
jsonMapper.fromJson(resultDb.data, listType) as T?
} else {
null
}
} else {
null
}
}
- 总结:
这个库非常容易上手, 还有另外两种模式,缓存同Activity生命周期结合起来,和直接从缓存中取出对象.