好了,我们已经写好了缓存加载流程。突然一愣,要是数据条数很多怎么办?难道要将数据一次性全部返回给客户端吗?这显然是不现实的,于是我们还得考虑分页缓存。
首先我们得理清楚一个概念,什么是分页缓存?它和缓存分页有什么区别?分页缓存是发生在缓存阶段,而缓存分页是发生在加载阶段,这是两个完全不同的概念。分页缓存是按页去请求数据,然后缓存起来,缓存分页是将缓存读取到livedata后,分批显示在UI上。
```
package dora.cache.repository
import android.content.Context
import android.util.Log
import androidx.lifecycle.MutableLiveData
import dora.cache.data.fetcher.IListDataFetcher
import dora.cache.holder.ListCacheHolder
import dora.db.builder.Condition
import dora.db.builder.QueryBuilder
import dora.db.table.OrmTable
import java.lang.IllegalArgumentException
/**
* 分页缓存的仓库,专门用来分页请求网络数据。
*/
abstract class DoraPageDatabaseCacheRepository<T : OrmTable>(context: Context)
: DoraDatabaseCacheRepository<T>(context) {
/**
* 页面编号,从0开始算。
*/
private var pageNo: Int = 0
/**
* 每页最多能承载的数据条数。
*/
private var pageSize: Int = 10
/**
* 是否仅更新新产生的数据到livedata。
*/
private var postNewDataOnly: Boolean = true
fun getPageNo(): Int {
return pageNo
}
fun getPageSize(): Int {
return pageSize
}
/**
* 改变页数。
*/
open fun setCurrentPage(pageNo: Int, pageSize: Int): DoraPageDatabaseCacheRepository<T> {
this.pageNo = pageNo
this.pageSize = pageSize
return this
}
fun setPostNewDataOnly(postNewDataOnly: Boolean): DoraPageDatabaseCacheRepository<T> {
this.postNewDataOnly = postNewDataOnly
return this
}
/**
* 分页查询缓存。
*/
override fun query(): Condition {
val start = pageNo * pageSize
val end = start + pageSize
return QueryBuilder.create()
.limit(start, end)
.toCondition()
}
/**
* 没网的情况下直接加载缓存数据。
*/
override fun selectData(ds: DataSource): Boolean {
var isLoaded = false
if (!isNetworkAvailable) {
isLoaded = ds.loadFromCache(DataSource.CacheType.DATABASE)
}
return if (isNetworkAvailable) {
try {
ds.loadFromNetwork()
true
} catch (e: Exception) {
Log.e(TAG, e.toString())
isLoaded
}
} else isLoaded
}
final override fun disallowForceUpdate(): Boolean {
return false
}
override fun parseModels(models: MutableList<T>?, listener: IListDataFetcher.OnLoadListener?,
liveData: MutableLiveData<MutableList<T>>) {
models?.let {
if (isLogPrint) {
for (model in it) {
Log.d(TAG, model.toString())
}
}
onInterceptData(DataSource.Type.NETWORK, it)
if (checkValuesNotNull()) {
// 移除之前所有条件的数据
for (condition in (listCacheHolder as ListCacheHolder).cacheConditions) {
listCacheHolder.removeOldCache(condition)
}
listCacheHolder.removeOldCache(query())
} else throw IllegalArgumentException("Query parameter would be null, checkValuesNotNull return false.")
// 使用追加缓存的模式
val temp = arrayListOf<T>()
liveData.value?.let { v ->
temp.addAll(v)
}
// 先拿到旧版本的内存中的数据,和新数据进行合并
temp.addAll(it)
// 然后将新旧版本的数据一起缓存下来
listCacheHolder.addNewCache(temp)
// 记录这一次的缓存条件
(listCacheHolder as ListCacheHolder).cacheConditions.add(query())
if (postNewDataOnly) {
// 仅更新追加的那部分数据到UI上
liveData.postValue(it)
} else {
// 更新最新的合并后的数据到UI上
liveData.postValue(temp)
}
}
listener?.onSuccess()
}
}
```
这里我们关键点是保存了旧版本的查询条件。如果退出程序然后断网,查询条件将会丢失,这时我们每次缓存的都是完整的数据就起作用了,按照pageNo和pageSize我们就可以完成分页数据的离线加载。
下面我们来设计缓存分页,这个功能是附加功能,通常也不和分页缓存同时使用。我们先定义一个接口IDataPager,用于对livedata的数据进行分页返回给UI显示。
```
package dora.cache.data.page
import dora.cache.data.visitor.IPageDataVisitor
interface IDataPager<M> {
/**
* 设置当前是第几页。
*
* @param currentPage 建议从0开始
*/
var currentPage: Int
/**
* 每页有几条数据?
*
* @return 不要返回0,0不能做除数
*/
var pageSize: Int
val models: List<M>
/**
* 加载过滤后的页面数据。
*/
fun loadData(models: List<M>)
/**
* 页面数据改变后,会回调它。
*/
fun onResult(result: (models: List<M>) -> Unit) : IDataPager<M>
/**
* 接收具体访问者的访问,不同的访问者将会以不同的规则呈现页面数据。
*/
fun accept(visitor: IPageDataVisitor<M>)
}
```
缓存分页使用到了Visitor角色,Visitor的引入可以不破坏原始数据的封装,通过改变Visitor的实现,来抽取不同的样本。我们再定义一个IPageDataVisitor接口。
```
package dora.cache.data.visitor
import dora.cache.data.page.IDataPager
/**
* 分页数据的访问者。
*
* @param <M>
*/
interface IPageDataVisitor<M> {
/**
* 访问数据分页器。
*
* @param pager
*/
fun visitDataPager(pager: IDataPager<M>)
/**
* 过滤出符合要求的一页数据。
*
* @param model 原始数据
* @param totalCount 数据总条数
* @param currentPage 当前第几页
* @param pageSize 每页数据条数
* @return 该页的数据
*/
fun filterPageData(models: List<M>, totalCount: Int, currentPage: Int, pageSize: Int): List<M>
}
```
这时我们再回到BaseRepository类看一下。
```
/**
* @see IListDataFetcher
*/
override fun obtainPager(): IDataPager<M> {
return listDataFetcher.obtainPager()
}
```
我们之前就给DataFetcher定义了一个方法用来获取数据分页器。
```
obtainPager().apply {
pageSize = 20
currentPage = 0
// 设置分页数据结果的回调
onResult {
// 在onResult中接收访问的数据
}
// 使用默认的分页访问者访问数据
accept(DefaultPageDataVisitor<OrmTable>())
}
```
只要accept方法一调用,onResult就会得到抽样的数据,瑞思拜。