移动设备在有限的功率和网络容量下运行,因此作为开发人员,我们的工作是优化这些资源的使用,以获得更高效和更流畅的性能。在构建 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 学习,面试文档,视频收集大整理,有兴趣的伙伴们可以看看~
如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言,一定会认真查询,修正不足,谢谢。