当前位置: 首页 > news >正文

Jetpack Paging 终极封装:简洁、通用、高性能的分页加载方案

为了简化 Jetpack Paging 的使用并提升代码的可维护性,可以对其进行封装,提供一个通用的分页加载工具类。以下是优化后的封装方案,支持与 Room 数据库、网络数据源的集成,并提供简洁的 API。

1. 封装思路
通用 PagingSource:封装通用的分页数据加载逻辑,支持 Room 和网络数据源。

PagingHelper:提供分页数据的创建和管理。

Repository 和 ViewModel 集成:通过简洁的 API 实现分页数据的加载和显示。

2. 封装代码
2.1 添加依赖

build.gradle 中添加 Paging 和 Room 依赖:

dependencies {
    def paging_version = "3.2.1"
    implementation "androidx.paging:paging-runtime:$paging_version"
    implementation "androidx.paging:paging-compose:$paging_version" // 如果使用 Jetpack Compose
    implementation "androidx.room:room-ktx:2.6.1" // Room 支持
}

2.2 定义通用 PagingSource
封装通用的分页数据加载逻辑:

import androidx.paging.PagingSource
import androidx.paging.PagingState

abstract class BasePagingSource<Key : Any, Value : Any> : PagingSource<Key, Value>() {

    abstract suspend fun loadData(params: LoadParams<Key>): LoadResult<Key, Value>

    override suspend fun load(params: LoadParams<Key>): LoadResult<Key, Value> {
        return try {
            loadData(params)
        } catch (e: Exception) {
            LoadResult.Error(e)
        }
    }

    override fun getRefreshKey(state: PagingState<Key, Value>): Key? {
        return state.anchorPosition?.let { anchorPosition ->
            state.closestPageToPosition(anchorPosition)?.prevKey
                ?: state.closestPageToPosition(anchorPosition)?.nextKey
        }
    }
}

2.3 定义 PagingHelper
封装分页数据的创建和管理:

import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import kotlinx.coroutines.flow.Flow

class PagingHelper<Key : Any, Value : Any>(
    private val pagingSourceFactory: () -> PagingSource<Key, Value>,
    private val pageSize: Int = 20,
    private val enablePlaceholders: Boolean = false
) {

    fun getPagingData(): Flow<PagingData<Value>> {
        return Pager(
            config = PagingConfig(
                pageSize = pageSize,
                enablePlaceholders = enablePlaceholders
            ),
            pagingSourceFactory = pagingSourceFactory
        ).flow
    }
}

2.4 定义 Repository
在 Repository 中使用 PagingHelper 创建分页数据流:

class UserRepository(private val userDao: UserDao) {

    fun getUsers(): Flow<PagingData<User>> {
        val pagingHelper = PagingHelper<Int, User>(
            pagingSourceFactory = { userDao.getUsers() }
        )
        return pagingHelper.getPagingData()
    }
}

2.5 定义 ViewModel
在 ViewModel 中观察分页数据:

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData
import androidx.paging.cachedIn
import kotlinx.coroutines.flow.Flow

class UserViewModel(private val repository: UserRepository) : ViewModel() {

    val users: Flow<PagingData<User>> = repository.getUsers()
        .cachedIn(viewModelScope) // 缓存数据,避免重复加载
}

2.6 在 Activity/Fragment 中绑定数据
使用 PagingDataAdapter 在 RecyclerView 中显示分页数据:

import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil

class MainActivity : AppCompatActivity() {

    private val viewModel: UserViewModel by viewModels()
    private lateinit var adapter: UserAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
        adapter = UserAdapter()
        recyclerView.adapter = adapter

        // 观察分页数据
        viewModel.users.observe(this) { pagingData ->
            adapter.submitData(lifecycle, pagingData)
        }
    }
}

class UserAdapter : PagingDataAdapter<User, UserViewHolder>(USER_COMPARATOR) {

    override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
        val user = getItem(position)
        user?.let { holder.bind(it) }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
        return UserViewHolder.create(parent)
    }

    companion object {
        private val USER_COMPARATOR = object : DiffUtil.ItemCallback<User>() {
            override fun areItemsTheSame(oldItem: User, newItem: User): Boolean {
                return oldItem.id == newItem.id
            }

            override fun areContentsTheSame(oldItem: User, newItem: User): Boolean {
                return oldItem == newItem
            }
        }
    }
}

2.7 支持网络数据源
如果需要从网络加载数据,可以自定义 PagingSource:

class UserPagingSource(private val apiService: ApiService) : BasePagingSource<Int, User>() {

    override suspend fun loadData(params: LoadParams<Int>): LoadResult<Int, User> {
        val page = params.key ?: 1
        val response = apiService.getUsers(page, params.loadSize)

        return LoadResult.Page(
            data = response.users,
            prevKey = if (page == 1) null else page - 1,
            nextKey = if (response.users.isEmpty()) null else page + 1
        )
    }
}

在 Repository 中使用:

class UserRepository(private val apiService: ApiService) {

    fun getUsers(): Flow<PagingData<User>> {
        val pagingHelper = PagingHelper<Int, User>(
            pagingSourceFactory = { UserPagingSource(apiService) }
        )
        return pagingHelper.getPagingData()
    }
}

3. 封装的优势
通用性:通过 BasePagingSource 和 PagingHelper,支持 Room 和网络数据源。

简洁性:提供简洁的 API,减少重复代码。

高性能:支持协程和 Flow,确保线程安全和高效加载。

易扩展:支持自定义 PagingSource 和 RemoteMediator,适用于复杂场景。

4. 进一步优化
4.1 支持依赖注入
结合 Dagger/Hilt,进一步解耦依赖:

@Module
@InstallIn(SingletonComponent::class)
object AppModule {

    @Provides
    @Singleton
    fun provideUserRepository(userDao: UserDao): UserRepository {
        return UserRepository(userDao)
    }
}

4.2 支持边界回调
通过 RemoteMediator 实现网络和本地数据库的混合分页加载。

5. 总结
通过封装 Jetpack Paging,可以显著简化分页加载的实现,提升代码的可维护性和扩展性。关键点包括:

使用 BasePagingSource 封装通用分页逻辑。

使用 PagingHelper 简化分页数据的创建。

结合 Room 和网络数据源,提供灵活的分页加载方案。

这套封装方案适用于大多数 Android 项目,能够显著提升开发效率和代码质量。


文章转载自:

http://l6xt8Znv.gftnx.cn
http://9yKZYylw.gftnx.cn
http://C94DnrIX.gftnx.cn
http://MPyoJvDw.gftnx.cn
http://jXcbCMoS.gftnx.cn
http://vIO0B9fR.gftnx.cn
http://R2j2AaN9.gftnx.cn
http://eEh28z4j.gftnx.cn
http://bcIG2plV.gftnx.cn
http://UxH6Rd3c.gftnx.cn
http://OI58erBz.gftnx.cn
http://UNjr4Uj7.gftnx.cn
http://upR2N1ym.gftnx.cn
http://bRfqtwo4.gftnx.cn
http://vV95Hqfc.gftnx.cn
http://zjbWQu27.gftnx.cn
http://HvSLK5cv.gftnx.cn
http://iB79sybA.gftnx.cn
http://3TfGMOO4.gftnx.cn
http://UOqaDD4T.gftnx.cn
http://B23hf62T.gftnx.cn
http://45YHklUz.gftnx.cn
http://RFLlMjdY.gftnx.cn
http://vVOFUQ4a.gftnx.cn
http://IeuHC5LU.gftnx.cn
http://GsJK1yDA.gftnx.cn
http://j62qG7Bz.gftnx.cn
http://akMaznzT.gftnx.cn
http://uXoAa7aI.gftnx.cn
http://Syr07yi5.gftnx.cn
http://www.dtcms.com/a/64909.html

相关文章:

  • 完整项目案例:基于Django的毕业设计选题管理系统(包含源码结构、核心代码及设计文档框架)
  • spring boot 发送邮件验证码
  • VSTO(C#)Excel开发4:打印设置
  • 【C/C++】相交链表(leetcode T160)
  • C语言学习笔记-进阶(13)动态内存管理2
  • 欢乐力扣:有效的括号
  • FX-C++可变参数
  • webRTC实现一对一通话视频流程
  • SpringBoot 自动配置原理
  • 电能质量有哪些指标?
  • 深入解析pnpm与npm:颠覆传统包管理的技术革命与应用实践
  • 深度解析:主流大模型与智能体框架能力对比及实战指南
  • DeepSeek结合Mermaid绘图(流程图、时序图、类图、状态图、甘特图、饼图)转载
  • WPF 与 GMap.NET 结合实现雷达目标动态显示与地图绘制
  • 混沌理论与混沌映射——算法改进初始化创新点之一
  • 48. 旋转图像(C++)
  • 面向对象Demo01
  • 混沌映射可行性恢复粒子群优化算法
  • Day31 | 139. 单词拆分、198. 打家劫舍、213. 打家劫舍 II、337. 打家劫舍 III
  • 微前端教程:使用 Single-SPA 构建模块化应用
  • 江科大51单片机笔记【10】DS1302时钟可调时钟(下)
  • 带权重的词典设计
  • 【Godot4.3】Geometry2D总结
  • 基于Vue 3的智能支付二维码弹窗组件设计与实现
  • MySQL事务深度解析:ACID特性、隔离级别与MVCC机制
  • 歌词相关实现
  • Vscode工具开发Vue+ts项目时vue文件ts语法报错-红波浪线等
  • 第n小的质数(信息学奥赛一本通-1099)
  • 【每日学点HarmonyOS Next知识】获取资源问题、软键盘弹起、swiper更新、C给图片设置位图、读取本地Json
  • 为AI聊天工具添加一个知识系统 之142 设计重审 之7 “我I”:mine/own/self