使用 Android Paging Library + Clean Architecture

126 篇文章 12 订阅

移动设备在有限的功率和网络容量下运行,因此作为开发人员,我们的工作是优化这些资源的使用,以获得更高效和更流畅的性能。在构建 android 应用程序时,我们经常遇到需要从数据源或 API 中提取数据并将它们显示为可滚动列表中的项目的情况。当这些项目的数量非常庞大甚至更糟时,就会出现问题:它总是在增加。

为了解决这个问题,android 提供了 Paging Library 作为 Android Jetpack 的一部分,它本质上是一个遵循延迟加载软件设计模式的库,允许我们推迟显示项目,直到需要在可滚动项目列表上显示。分页库管理网络调用、数据缓存以及列表数据的重新加载/刷新。非常棒,对吧?现在,让我们看一些代码。

Models

首先,我们需要定义我们的数据容器,这将是我们业务逻辑的抽象。在这种情况下,我们将使用一个Person包含人名和年龄的类。接下来,我们将定义一个包含远程数据源响应的网络响应类,我们将调用它PeopleResponse

data class Person(val name:String, val age: int)

class PeopleResponse{
    @SerializedName("next_page")
    val nextPage: Int?
    @SerializedName("previous_page")
    val perviousPage: Int?
    @SerializedName("people") 
    val people: List<Person>
}

远程数据源

远程数据源是负责网络调用以获取数据以加载要在列表中显示的项目的服务。在我们的例子中,创建一个带有单个getPeople函数的 Retrofit 接口,该函数将页码作为参数。

interface PeopleService {
    @GET("people/{page}")
    fun getPeople(page:Int): PeopleResponse

    companion object {
        operator fun invoke(): PeopleService {
             val builder = Retrofit.Builder().baseUrl(baseUrl)
             // we need to add converter factory to 
             // convert JSON object to Java object
            .addConverterFactory(GsonConverterFactory.create()) 
            .build()
            .create(PeopleService::class.java)
        }
    }
}

PagingSource

接下来我们通过扩展 Jetpack 的PagingSource类来创建我们的分页源。寻呼源将是负责加载和同步网络调用以及缓存和刷新数据的类。我们需要实现两个功能:

加载

在需要新页面时调用,并包含指定加载信息的参数,例如当前页面键。

获取刷新键

当前PagingSource无效时调用,返回Int页面的键以在新创建的分页源中使用。如果null返回,PagingSource则从第一页开始。

class PeoplePagingSource(
    val service: PeopleService,
) : PagingSource<Int, Person>() {
  override suspend fun load(
    params: LoadParams<Int>
  ): LoadResult<Int, User> {
    try {
      // Should start at 1 if no key is found
      val nextPage = params.key ?: 1
      val response = service.getPeople(nextPage)
      return LoadResult.Page(
        data = response.users,
        prevKey = response.previousPage, 
        nextKey = response.nextPage
      )
    } catch (e: Exception) {
      // Handle errors e.g: Network Failure
    }
  }

  override fun getRefreshKey(state: PagingState<Int, Person>): Int? {
    // Try to get the closest page to the current anchorPosition
    return state.anchorPosition?.let { anchorPosition ->
    state.closestPageToPosition(anchorPosition)?.let { anchorPage ->
      val pageIndex = pages.indexOf(anchorPage)
          if (pageIndex == 0) {
            null
          } else {
            pages[pageIndex - 1].nextKey
          }
        }
      }
    }
  }
}

公开 PagingData

在我们成功设置之后,PagingSource就该遵循 MVVM 最佳实践并将结果公开为 Kotlin 流以供我们的 UI 使用。我们在 ViewModel 中设置如下:

class PeopleViewModel(val service: PeopleService) : ViewModel() {
    val people = Pager(
      // Configure how data should be loaded
      PagingConfig(pageSize = 20)
    ) {
      PeoplePagingSource(service)
    }.flow.cachedIn(viewModelScope)
}

更新用户界面

最后,UI 组件,例如:Fragment,应该使用分页数据并相应地更新 List。这样,每当我们在滚动时用完项目时,分页库将自动启动网络请求,缓存它们,并RecyclerView Adapter相应地更新它们。

class PeopleFragment : Fragment(){

    val viewModel by viewModels<PeopleViewModel>()

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
         val pagingAdapter = PeopleAdapter(UserComparator)
         val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
         recyclerView.adapter = pagingAdapter

         viewLifecycleOwner.lifecycleScope..launch {
             viewModel.flow.collectLatest { pagingData ->
                 pagingAdapter.submitData(pagingData)
           } 
        }
    return inflater.inflate(R.layout.fragment_people, container, false)
    }
}

最后,如果大伙有什么好的学习方法或建议欢迎大家在评论中积极留言哈,希望大家能够共同学习、共同努力、共同进步。

小编在这里祝小伙伴们在未来的日子里都可以 升职加薪,当上总经理,出任CEO,迎娶白富美,走上人生巅峰!!

不论遇到什么困难,都不应该成为我们放弃的理由!

很多人在刚接触这个行业的时候或者是在遇到瓶颈期的时候,总会遇到一些问题,比如学了一段时间感觉没有方向感,不知道该从那里入手去学习,需要一份小编整理出来的学习资料的关注我主页或者点击微信卡片免费领取~

这里是关于我自己的Android 学习,面试文档,视频收集大整理,有兴趣的伙伴们可以看看~

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言,一定会认真查询,修正不足,谢谢。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个使用 KotlinPaging3+RecyclerView 实现数据分页的示例: 1. 首先,我们需要定义一个包含数据的列表和当前页码的类,使用 PagingData 类型: ``` data class Data( val id: Int, val name: String, val description: String ) ``` 2. 接下来,我们需要定义一个 PagingSource,它负责从数据源获取数据: ``` class DataPagingSource(private val api: DataApi) : PagingSource<Int, Data>() { override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Data> { try { val page = params.key ?: 1 val pageSize = params.loadSize val response = api.getData(page, pageSize) val data = response.data val prevKey = if (page == 1) null else page - 1 val nextKey = if (data.isEmpty()) null else page + 1 return LoadResult.Page( data = data, prevKey = prevKey, nextKey = nextKey ) } catch (e: Exception) { return LoadResult.Error(e) } } override fun getRefreshKey(state: PagingState<Int, Data>): Int? { return state.anchorPosition?.let { anchorPosition -> val anchorPage = state.closestPageToPosition(anchorPosition) anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1) } } } ``` 3. 然后,我们需要定义一个 PagingData 类型的数据流,并使用它创建一个 PagingDataAdapter: ``` class DataAdapter : PagingDataAdapter<Data, DataViewHolder>(DataDiffCallback) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DataViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.item_data, parent, false) return DataViewHolder(view) } override fun onBindViewHolder(holder: DataViewHolder, position: Int) { val item = getItem(position) holder.bindData(item) } object DataDiffCallback : DiffUtil.ItemCallback<Data>() { override fun areItemsTheSame(oldItem: Data, newItem: Data): Boolean { return oldItem.id == newItem.id } override fun areContentsTheSame(oldItem: Data, newItem: Data): Boolean { return oldItem == newItem } } } ``` 4. 最后,在 Activity 或 Fragment 中使用 RecyclerView 和 DataAdapter: ``` class MainActivity : AppCompatActivity() { private lateinit var recyclerView: RecyclerView private lateinit var adapter: DataAdapter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) recyclerView = findViewById(R.id.recycler_view) recyclerView.layoutManager = LinearLayoutManager(this) adapter = DataAdapter() recyclerView.adapter = adapter val api = DataApi() val pagingSourceFactory = { DataPagingSource(api) } val pagingConfig = PagingConfig(pageSize = 10) val dataFlow = Pager( config = pagingConfig, pagingSourceFactory = pagingSourceFactory ).flow dataFlow.cachedIn(lifecycleScope).collectLatest { pagingData -> adapter.submitData(pagingData) } } } ``` 这是一个简单的示例,你可以根据自己的需求进行修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值