Android数据缓存框架 - 分页缓存与缓存分页的区别

文章讨论了在Android应用中处理大量数据时的分页缓存策略。通过DoraPageDatabaseCacheRepository类,展示了如何按页请求和存储数据,以及在无网络连接时如何加载离线数据。同时,提出了IDataPager接口和IPageDataVisitor接口,用于实现缓存数据的分页展示,允许通过不同的访问者规则呈现页面数据。
摘要由CSDN通过智能技术生成

好了,我们已经写好了缓存加载流程。突然一愣,要是数据条数很多怎么办?难道要将数据一次性全部返回给客户端吗?这显然是不现实的,于是我们还得考虑分页缓存。

首先我们得理清楚一个概念,什么是分页缓存?它和缓存分页有什么区别?分页缓存是发生在缓存阶段,而缓存分页是发生在加载阶段,这是两个完全不同的概念。分页缓存是按页去请求数据,然后缓存起来,缓存分页是将缓存读取到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就会得到抽样的数据,瑞思拜。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dora丶Android

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值