Android分页查询数据库方案

219 阅读3分钟

在 Android 项目中,展示本地数据是一个常见需求。然而,当数据量较大时,如果我们不采用分页加载的方式,应用加载速度会显著下降,用户等待时间会增加,从而影响整体用户体验。因此,对于本地数据的分页加载处理,分页技术变得尤为重要。

图片

本文将介绍两种实现本地数据分页的方案:

  1. 通过查询语句实现分页
  2. 通过 Paging3 与 Room 结合实现分页

 

方案一:通过查询语句实现分页

这种方法通过 Room 的 SQL 查询直接实现分页,是较为传统的做法。我们首先定义对应的 Room 查询方法:

Room 查询的定义

@Dao
interface ArticleDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertAll(articles: List<Article>)
 
    @Query("SELECT * FROM articles ORDER BY id ASC LIMIT :limit OFFSET :offset")
    fun getArticlesInRange(limit: Int, offset: Int): List<Article>
}

在此基础上,通过自定义 PagingSource 来实现分页功能。

自定义 PagingSource 实现

class LoadPagingSource(private val pageCount:Int = 10): PagingSource<Int, DownloadEntity>() {

   override fun getRefreshKey(state: PagingState<Int, DownloadEntity>): Int? {
       return null
   }

   override suspend fun load(params: LoadParams<Int>): LoadResult<Int, DownloadEntity> {
       return try {
           val page = params.key ?: FIRST_PAGE_INDEX
           val taskList = ArticleDao.getArticlesInRange(page, 10)
           if (taskList.isNullOrEmpty()){
               if (page == 1){
                   LoadResult.Page(data = emptyList(), prevKey = 0, nextKey = 2)
               }else{
                   LoadResult.Error(ServerException("taskList.isNullOrEmpty()"))
               }
           }else{
               LoadResult.Page(data = taskList, prevKey = null, nextKey = null)
           }
       }catch (t:Throwable){
           LoadResult.Error(ServerException(t))
       }


   }

   companion object {
       private const val FIRST_PAGE_INDEX = 1
   }
}

优点与不足

  1. 优点:
    • 直接操作 Room 数据库,逻辑清晰简单。
    • 易于控制分页的实现细节,比如分页大小和偏移量。
  2. 不足:
    • 数据库更新后无法自动反映,需要手动重新加载数据。
    • 如果数据量较大,查询效率可能不够高,用户体验受限。

对于实时性要求较高的场景,该方案可能无法完全满足需求,因此我们可以考虑更高级的方案——Paging3 与 Room 结合。

方案二:通过 Paging3 与 Room 结合实现分页

Paging3 是 Jetpack 提供的一个强大的分页加载库,与 Room 深度集成后,可以轻松实现高效、实时的分页加载。与第一种方案相比,Paging3 自动处理数据变更并支持更复杂的分页操作。

Room 数据源定义

@Daointerface ArticleDao {    @Query("SELECT * FROM articles ORDER BY id ASC")
    fun getPagedRecords(): PagingSource<Int, Article>
}

Room 提供的 PagingSource 会自动根据分页配置进行查询,无需额外处理。

使用 Paging3 配置分页

以下是如何通过 Paging3 配置分页的示例:

fun getListLocalDataByPage(dao: RecordDao): Flow<PagingData<Record>> {
   preTime = 0L
   return Pager(
       config = PagingConfig(
           pageSize = 30,
           enablePlaceholders = false
       ),
       pagingSourceFactory = { dao.getPagedRecords() }
   ).flow.cachedIn(viewModelScope)
}

Paging3 与 Room 结合的优势

  1. 实时数据更新:
  2. Room 的 PagingSource 会监听数据库的变化。当数据更新(如插入或删除)时,UI 会自动刷新,不需要手动重新加载数据。
  3. 性能优化:
  4. Paging3 会自动管理分页数据的内存使用,按需加载数据并回收不必要的资源,从而优化大数据量场景的性能。
  5. 扩展性强:
  6. 可以方便地与在线数据源结合,支持多数据源混合分页。 提供了丰富的配置选项,如占位符、分页大小等,可以满足不同场景的需求。
  7. 简化开发:
  8. 通过与 Room 的集成,开发者无需再手动管理分页逻辑,只需专注于业务逻辑。

对比与总结

特点查询语句实现分页Paging3 与 Room 结合
实现复杂度需要手动处理分页逻辑,代码量较多通过集成 Room 和 Paging3,代码更简洁
性能查询效率较低,大数据量场景可能存在性能瓶颈高效内存管理和分页加载,适合大数据量场景
数据更新支持数据库更新需手动刷新数据库变化自动监听,UI 自动刷新
开发难度简单直观,适合简单场景稍有门槛,但功能强大,适合复杂场景

建议:

  • 如果项目中数据量较少且变更不频繁,可以使用查询语句实现分页。
  • 如果数据量大或需要实时更新,优先选择 Paging3 与 Room 的结合方案。

原文地址:mp.weixin.qq.com/s/_xat9S6Wp…