分页缓存与下拉刷新的整合原理 - DoraPageDatabaseCacheRepository

何为分页缓存?

顾名思义,分页缓存就是边分页边缓存,分页通常使用下拉刷新控件实现,而缓存通常说的是指磁盘缓存,即保存到数据库中,数据库本身也是一个索引文件。

为什么缓存还要分页?

在很大一部分场景下,缓存都是需要分页的,不要问我为什么,问就是不懂规矩,哈哈。你家的接口一次性把整张表的数据全部都返回给你吗?先不说用户耗多少流量的问题,如果大家都一次性把表的数据都给你,那爬虫何以生存?分分钟就把你的数据全都弄走了,要知道,互联网世界,数据就是资源,数据就是钱啊!

怎么使用下拉刷新分页?

重复的问题不会再讲,看我之前的文章Android低代码开发 - 直接创建一个下拉刷新列表界面。下拉刷新和上拉加载都是用来分页加载数据的。下拉刷新往往只加载第一页的数据,而上拉加载则是加载下一页的数据,如果有。

下拉刷新怎么和缓存结合起来?

这个问题问的好。本篇提到的缓存一律指数据库缓存,而非内存缓存。当然我的dcache库https://github.com/dora4/dcache-android 支持内存缓存,会简单带过。我以缓存系统通知列表为例。

package com.dorachat.dorachat.repositoryimport android.content.Context
import com.dorachat.dorachat.common.AppConfig.Companion.PRODUCT_NAME
import com.dorachat.dorachat.http.ApiResult
import com.dorachat.dorachat.http.service.HomeService
import com.dorachat.dorachat.model.SystemNotification
import dora.cache.data.adapter.ListResultAdapter
import dora.cache.data.fetcher.OnLoadStateListener
import dora.cache.factory.DatabaseCacheHolderFactory
import dora.cache.repository.DoraPageDatabaseCacheRepository
import dora.cache.repository.ListRepository
import dora.http.DoraListCallback
import dora.http.retrofit.RetrofitManager.getService
import retrofit2.Callback
import javax.inject.Inject@ListRepository
class SysNotificationRepository @Inject constructor(context: Context) :DoraPageDatabaseCacheRepository<SystemNotification>(context) {override fun onLoadFromNetwork(callback: DoraListCallback<SystemNotification>,listener: OnLoadStateListener?) {getService(HomeService::class.java).getSystemNotificationList(PRODUCT_NAME).enqueue(ListResultAdapter<SystemNotification, ApiResult<SystemNotification>>(callback)as Callback<ApiResult<MutableList<SystemNotification>>>)}override fun createCacheHolderFactory(): DatabaseCacheHolderFactory<SystemNotification> {return DatabaseCacheHolderFactory(SystemNotification::class.java)}
}

缓存仓库,使用@ListRepository标记缓存的数据结构是列表类型的。使用咱们的DoraPageDatabaseCacheRepository,进行分页数据的缓存。如果你不使用框架内置的ORM框架,则需要先整合你的ORM框架,具体看我专栏的其他文章。这个类就是为了告诉框架,你要缓存的数据结构是一个List类型的SystemNotification,而且会一页一页给到,请帮我缓存好了。我们知道list模式的BaseRepository使用流程是,先在activity设置一个数据监听器,repository.getListLiveData().observe(this, observer),然后改变一下参数,repository.setXxx(),调用一下repository.fetchListData()。一旦调用抓取数据的函数,就会调用onLoadFromNetwork()方法,加载最新参数的数据,这边的数据监听器就会被回调,同时将数据缓存一份到数据库中,以备无网络的时候使用。附上DoraPageDatabaseCacheRepository的源码你就明白了。

package dora.cache.repositoryimport android.content.Context
import android.util.Log
import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.RecyclerView
import dora.cache.data.fetcher.OnLoadStateListener
import dora.db.builder.Condition
import dora.db.builder.QueryBuilder
import dora.db.table.OrmTableabstract class DoraPageDatabaseCacheRepository<T : OrmTable>(context: Context): DoraDatabaseCacheRepository<T>(context) {private var pageNo: Int = 0private var pageSize: Int = 10fun getPageNo(): Int {return pageNo}fun getPageSize(): Int {return pageSize}fun isLastPage(totalSize: Int) : Boolean {val lastPage = if (totalSize % pageSize == 0) totalSize / pageSize - 1 else totalSize / pageSizereturn lastPage == pageNo}fun observeData(owner: LifecycleOwner, adapter: AdapterDelegate<T>) {getListLiveData().observe(owner) {if (pageNo == 0) {adapter.setList(it)} else {adapter.addData(it)}}}interface AdapterDelegate<T> {fun setList(data: MutableList<T>)fun addData(data: MutableList<T>)}/*** 下拉刷新回调,可结合[setPageSize]使用。*/fun onRefresh(listener: OnLoadStateListener) {pageNo = 0fetchListData(listener = listener)}/*** 下拉刷新高阶函数,可结合[setPageSize]使用。*/@JvmOverloadsfun onRefresh(block: ((Boolean) -> Unit)? = null) {pageNo = 0fetchListData(listener = object : OnLoadStateListener {override fun onLoad(state: Int) {block?.invoke(state == OnLoadStateListener.SUCCESS)}})}/*** 上拉加载回调,可结合[setPageSize]使用。*/fun onLoadMore(listener: OnLoadStateListener) {pageNo++fetchListData(listener = listener)}/*** 上拉加载高阶函数,可结合[setPageSize]使用。*/@JvmOverloadsfun onLoadMore(block: ((Boolean) -> Unit)? = null) {pageNo++fetchListData(listener = object : OnLoadStateListener {override fun onLoad(state: Int) {block?.invoke(state == OnLoadStateListener.SUCCESS)}})}open fun setPageSize(pageSize: Int): DoraPageDatabaseCacheRepository<T> {this.pageSize = pageSizereturn this}open fun setCurrentPage(pageNo: Int, pageSize: Int): DoraPageDatabaseCacheRepository<T> {this.pageNo = pageNothis.pageSize = pageSizereturn this}override fun query(): Condition {val start = pageNo * pageSizereturn QueryBuilder.create().limit(start, pageSize).toCondition()}/*** 没网的情况下直接加载缓存数据。*/override fun selectData(ds: DataSource): Boolean {var isLoaded = falseif (!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}
}

在这个分页缓存仓库类中,定义了分页的一些参数,如第几页?每页几条数?onRefresh()和onLoadMore()完全就是为了配合下拉刷新控件而设计的。不会使用dcache库的,是不是看源码也能知道怎么调用?observeData()方法是不是就是我们刚才讲到的数据监听器,或者说观察者。那么就来看一下调用层面的代码吧。

package com.dorachat.dorachat.ui.activity.adminimport android.app.Activity
import android.content.Intent
import android.os.Build
import android.os.Bundle
import androidx.annotation.RequiresApi
import com.dorachat.dorachat.ChatAppimport com.dorachat.dorachat.R
import com.dorachat.dorachat.common.AppConfig.Companion.ACTION_UPDATE_SYS_NOTIFICATION
import com.dorachat.dorachat.common.AppConfig.Companion.PRODUCT_NAME
import com.dorachat.dorachat.common.AppConfig.Companion.REQUEST_CODE_ADD_SYS_NOTIFICATION
import com.dorachat.dorachat.common.AppConfig.Companion.REQUEST_CODE_UPDATE_SYS_NOTIFICATION
import com.dorachat.dorachat.common.IntentKeys.Companion.KEY_SYSTEM_NOTIFICATION
import com.dorachat.dorachat.databinding.ActivitySysNotificationBinding
import com.dorachat.dorachat.di.component.DaggerUserComponent
import com.dorachat.dorachat.http.service.HomeService
import com.dorachat.dorachat.model.SystemNotification
import com.dorachat.dorachat.model.request.ReqSysNotification
import com.dorachat.dorachat.repository.SysNotificationRepository
import com.dorachat.dorachat.ui.adapter.SysNotificationAdapter
import dora.cache.repository.DoraPageDatabaseCacheRepository
import dora.dagger.DaggerBaseActivity
import dora.http.DoraHttp
import dora.http.DoraHttp.net
import dora.http.retrofit.RetrofitManager
import dora.util.IntentUtils
import dora.util.ViewUtils
import dora.widget.DoraAlertDialog
import dora.widget.Tips
import dora.widget.pull.SwipeLayout
import javax.inject.Injectclass SysNotificationActivity : DaggerBaseActivity<ActivitySysNotificationBinding>() {@Inject lateinit var notificationRepository: SysNotificationRepositoryprivate val adapter = SysNotificationAdapter()override fun getLayoutId(): Int {return R.layout.activity_sys_notification}override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {super.onActivityResult(requestCode, resultCode, data)if (resultCode == Activity.RESULT_OK) {if (requestCode == REQUEST_CODE_ADD_SYS_NOTIFICATION ||requestCode == REQUEST_CODE_UPDATE_SYS_NOTIFICATION) {notificationRepository.onRefresh()}}}override fun onInjectDaggerComponent() {DaggerUserComponent.builder().appComponent(ChatApp.appComponent).build().inject(this)}@RequiresApi(Build.VERSION_CODES.O)override fun initData(savedInstanceState: Bundle?, binding: ActivitySysNotificationBinding) {binding.tvSysNotificationAdd.setOnClickListener {IntentUtils.startActivityForResult(SysNotificationEditorActivity::class.java, REQUEST_CODE_ADD_SYS_NOTIFICATION)}notificationRepository.observeData(this, object : DoraPageDatabaseCacheRepository.AdapterDelegate<SystemNotification> {override fun addData(data: MutableList<SystemNotification>) {adapter.addData(data)mBinding.emptyLayout.showContent()}override fun setList(data: MutableList<SystemNotification>) {adapter.setList(data)mBinding.emptyLayout.showContent()}})binding.slSysNotificationList.setOnSwipeListener(object : SwipeLayout.OnSwipeListener {override fun onRefresh(swipeLayout: SwipeLayout) {}override fun onLoadMore(swipeLayout: SwipeLayout) {if (!notificationRepository.isLastPage(notificationRepository.getTotalSize())) {notificationRepository.onLoadMore {swipeLayout.loadMoreFinish(if (it) SwipeLayout.SUCCEED else SwipeLayout.FAIL)}}}})ViewUtils.configRecyclerView(binding.recyclerView).adapter = adapternotificationRepository.onRefresh()adapter.addChildClickViewIds(R.id.ll_sys_notification_list, R.id.btn_delete)adapter.setOnItemChildClickListener { _, view, position ->when (view.id) {R.id.ll_sys_notification_list -> {val item = adapter.getItem(position)IntentUtils.startActivityForResultWithSerializable(this@SysNotificationActivity,SysNotificationEditorActivity::class.java,ACTION_UPDATE_SYS_NOTIFICATION,REQUEST_CODE_UPDATE_SYS_NOTIFICATION,KEY_SYSTEM_NOTIFICATION, item)}R.id.btn_delete -> {DoraAlertDialog(this).show(R.string.confirm_delete) {themeColorResId(R.color.colorPrimary)positiveListener {val item = adapter.getItem(position)net {val req = ReqSysNotification(productName = PRODUCT_NAME,id = item.id)val ok = DoraHttp.result {RetrofitManager.getService(HomeService::class.java).removeSystemNotification(req.toRequestBody())}?.dataif (ok == true) {adapter.removeAt(position)Tips.showSuccess(R.string.deleted_successfully)}}}}}}}}
}

这里用到了Dagger的依赖注入,不在本篇的讨论范围。DoraPageDatabaseCacheRepository.AdapterDelegate告诉适配器,你就在我的回调里面更新数据就好了。

在哪里去找缓存库相关源码?

https://github.com/dora4/dcache-android 缓存库

https://github.com/dora4/dview-swipe-layout 下拉刷新控件

https://github.com/dora4/dview-empty-layout 空态页面

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/38458.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SSH 无密登录配置流程

一、免密登录原理 非对称加密&#xff1a; 由于对称加密的存在弊端&#xff0c;就产生了非对称加密&#xff0c;非对称加密中有两个密钥&#xff1a;公钥和私钥。公钥由私钥产生&#xff0c;但却无法推算出私钥&#xff1b;公钥加密后的密文&#xff0c;只能通过对应的私钥来解…

光速入门 Tailwind CSS

文章目录 入门安装IDE 设置使用预编译器生产环境优化 基础概念分层指令tailwindlayerapplyconfig 函数theme()screen() 基础案例怎么设置属性任意值&#xff1f;hover 父元素时&#xff0c;怎么选中子元素添加样式&#xff1f;添加 animation 动画 配置主题 Tailwind CSS 中文网…

.so: file not recognized: file format not recognized

项目场景&#xff1a; 自Linux 4.8起&#xff0c;传统的GPIO sysfs接口被弃用。libgpiod操作gpio的方式感觉更加方便。 但是单板上好像没装这个工具&#xff0c;又到了熟悉的交叉编译环节&#xff08;痛苦&#xff09;。 问题描述 按照流程装完libgpiod&#xff0c;自信地去交…

安卓Gradle学习与应用:从入门到实践

引言 在Android开发的世界里&#xff0c;Gradle不仅仅是一个构建工具&#xff0c;它更是一种强大的自动化系统&#xff0c;能够帮助开发者高效地管理项目依赖、编译、测试、打包以及部署。本篇博客旨在深入探讨Gradle在Android开发中的应用&#xff0c;从基础概念到实战技巧&a…

334. 递增的三元子序列

334. 递增的三元子序列 题目链接&#xff1a;334. 递增的三元子序列 代码如下&#xff1a; class Solution { public://贪心bool increasingTriplet(vector<int>& nums) {if(nums.size()<3) {return false;}int firstnums[0],secondINT_MAX;for(int i1;i<…

Go源码--context包

简介 Context 是go语言比较重要的且也是比较复杂的一个结构体&#xff0c;Context主要有两种功能: 取消信号&#xff1a;包括直接取消&#xff08;涉及的结构体&#xff1a;cancelCtx ; 涉及函数&#xff1a;WithCancel&#xff09;和携带截止日期的取消&#xff08;涉及结构…

密室逃脱——收集版

一、原版修改 1、导入资源 Unity Learn | 3D Beginner: Complete Project | URP 2、设置Scene 删除SampleScene&#xff0c;打开UnityTechnologies-3DBeginnerComplete下的MainScene 3、降低音量 (1) 打开Hierarchy面板上的Audio降低音量 (2) 打开Prefabs文件夹&#xf…

Git安装与使用及整合IDEA使用的详细教程

1. 版本控制软件介绍 版本控制软件提供完备的版本管理功能&#xff0c;用于存储、追踪目录&#xff08;文件夹&#xff09;和文件的修改历史&#xff0c;是软件开发者的必备工具&#xff0c;是软件公司的基础设施。版本控制软件的最高目标&#xff0c;是支持软件公司的配置管理…

第三天:LINK3D核心原理讲解【第2部分】

三、 变量 // 点云容器 pcl::PointCloud<pcl::PointXYZI> laserCloud; // 一帧原始点云 pcl::PointCloud<pcl::PointXYZI> cornerPointsLessSharp; // 次极大边线点 pcl::PointCloud<pcl::PointXYZI> surfPointsLessFlat; // 次极小平面点 pcl::PointCloud&…

ubuntu中后台启动一个jar

1.使用 nohup 和 & 启动应用程序&#xff1a; nohup java -jar 你的jar包.jar > output.log 2>&1 &解释&#xff1a; nohup&#xff1a;忽略挂起信号&#xff08;SIGHUP&#xff09;&#xff0c;使进程在退出终端后继续运行。java -jar lxyoj-code-sandbox-…

管理统计学

第1章 统计学是收集、处理、分析、解释数据并从数据中得出结论的科学。 统计学是处理数据的方法论。 参数 表示总体特征的概括性数字度量&#xff0c;是研究者想要了解的总体的某种特征值。 统计量 是用来描述样本特征的概括性数字度量。 常用统计量包括&#xff1a; &#xff…

达梦数据库系列—14. 表空间的备份和还原

目录 1、表空间备份 2、表空间还原 3、表空间恢复 4、增量还原恢复 1、表空间备份 表空间只能在联机状态下进行备份。 BACKUP TABLESPACE TBS BACKUPSET /dm/backup/dm_bak/ts_bak_01; 完全备份 BACKUP TABLESPACE TBS FULL BACKUPSET /dm/backup/dm_bak/ts_full_bak_01…

ESP8266[ 关于-巴发云MQTT/TCP:arduino 设置回调函数 ] 日志2024/6/29

ESP8266 [ 关于-巴发云MQTT/TCP:arduino 设置回调函数 ] 日志2024/6/29 arduino库:#include <PubSubClient.h> 回调函数 是其库设置好的 可以改名字 这里只写上关键代码 设置客户端为 A 关键代码: A.setCallback(回调名) //MQTT 回调处理mqttmsgg(自定义…

zdppy_api+vue3实现前后端分离的登录功能

实现思路 1、准备zdppy的开发环境 2、使用amauth提供的低代码接口&#xff0c;直接生成login登录接口 3、使用之前开发的登录模板渲染登录界面 4、给登录按钮绑定点击事件 5、给用户名和密码的输入框双向绑定数据 6、使用axios在登录按钮点击的时候&#xff0c;携带用户数据发…

PySide(PyQt)与OpenCV图像格式的相互转换

PySide和OpenCV在图像格式上的区别&#xff1a; 主要表现在图像数据的存储方式和使用场景上。以下是一些关键区别&#xff1a; 1. 数据结构 PySide: QImage 和 QPixmap 是 PySide 中常用的图像表示形式。 QImage&#xff1a;用于直接访问图像的像素数据&#xff0c;适合需要…

C++ | Leetcode C++题解之第207题课程表

题目&#xff1a; 题解&#xff1a; class Solution { private:vector<vector<int>> edges;vector<int> indeg;public:bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {edges.resize(numCourses);indeg.resize(numCo…

MyBatis(15)MyBatis 的延迟加载是如何实现

MyBatis 的延迟加载&#xff08;懒加载&#xff09;特性允许在需要使用关联对象数据时才进行加载&#xff0c;而不是在执行主查询时就加载所有相关数据。这种机制可以提高应用程序的性能&#xff0c;特别是当关联数据庞大或关联层次较深时。我们将通过以下几个方面来深入了解My…

昇思25天学习打卡营第13天|MindNLP ChatGLM-6B StreamChat

学AI还能赢奖品&#xff1f;每天30分钟&#xff0c;25天打通AI任督二脉 (qq.com) MindNLP ChatGLM-6B StreamChat 本案例基于MindNLP和ChatGLM-6B实现一个聊天应用。 1 环境配置 %%capture captured_output # 实验环境已经预装了mindspore2.2.14&#xff0c;如需更换mindspo…

[知识点篇]《计算机组成原理》之数据信息的表示

1、数据表示的作用 &#xff08;1&#xff09;定义&#xff1a;将数据按照某种方式组织&#xff0c;以便机器硬件能直接识别和使用。现代计算机采用二进制进行数据表示。 &#xff08;2&#xff09;数据表示考虑因素&#xff1a; 数据的类型&#xff1a; 数值/非数值、小数、…

读AI新生:破解人机共存密码笔记17不确定性和概率

1. 前向搜索 1.1. 通过前向搜索&#xff0c;通过考虑各种可能的动作序列的结果&#xff0c;来选择动作&#xff0c;是智能系统的基本能力 1.2. 如果一家卡车运输公司想要优化其100辆卡车在美国的运输&#xff0c;那么该公司可能需要考虑的状态数量将是10^700个 1.3. 几乎所有…