Android(安卓)Jetpack之Paging初探
原文地址:https://www.loongwind.com/archives/367.html
Paging是Google 2018 IO大会最新发布的Jetpack中的一个组件,主要用于大数据的分页加载,这篇文章就来探索一下关于Paging的简单使用。
Paging介绍
Paging主要由三个部分组成:DataSource
PageList
PageListAdapter
DataSource
DataSource
从字面意思理解是一个数据源,其中key
对应加载数据的条件信息,Value
对应加载数据的实体类。DataSource
是一个抽象类,但是我们不能直接继承它实现它的子类。但是Paging库里提供了它的三个子类供我们继承用于不同场景的实现:
PageKeyedDataSource
:适用于目标数据根据页信息请求数据的场景,即Key
字段是页相关的信息。比如请求的数据的参数中包含类似next/previous
页数的信息。ItemKeyedDataSource
:适用于目标数据的加载依赖特定item的信息, 即Key字段包含的是Item中的信息,比如需要根据第N项的信息加载第N+1项的数据,传参中需要传入第N项的ID时,该场景多出现于论坛类应用评论信息的请求。PositionalDataSource
:适用于目标数据总数固定,通过特定的位置加载数据,这里Key是Integer类型的位置信息,T即Value。 比如从数据库中的1200条开始加在20条数据。
PageList
PageList
是一个List的子类,支持所有List的操作,除此之外它主要有五个成员:mMainThreadExecutor
: 一个主线程的Excutor, 用于将结果post到主线程。
mBackgroundThreadExecutor
: 后台线程的Excutor.
BoundaryCallback
:加载Datasource中的数据加载到边界时的回调.
Config
: 配置PagedList从Datasource加载数据的方式, 其中包含以下属性:
pageSize
:设置每页加载的数量prefetchDistance
:预加载的数量,默认为pagesize
initialLoadSizeHint
:初始化数据时加载的数量,默认为pageSize*3
enablePlaceholders
:当item为null是否使用PlaceHolder展示
PagedStorage
: 用于存储加载到的数据,它是真正的蓄水池所在,它包含一个ArrayList 对象mPages,按页存储数据。
PagedList会从Datasource中加载数据,更准确的说是通过Datasource加载数据, 通过Config的配置,可以设置一次加载的数量以及预加载的数量。 除此之外,PagedList还可以向RecyclerView.Adapter发送更新的信号,驱动UI的刷新。
PagedListAdapter
PagedListAdapte是RecyclerView.Adapter的实现,用于展示PagedList的数据。它本身实现的更多是Adapter的功能,但是它有一个小伙伴PagedListAdapterHelper
, PagedListAdapterHelper会负责监听PagedList的更新, Item数量的统计等功能。这样当PagedList中新一页的数据加载完成时, PagedAdapte就会发出加载完成的信号,通知RecyclerView刷新,这样就省略了每次loading后手动调一次notifyDataChanged()
.
除此之外,当数据源变动产生新的PagedList,PagedAdapter会在后台线程中比较前后两个PagedList的差异,然后调用notifyItem…()方法更新RecyclerView.这一过程依赖它的另一个小伙伴ListAdapterConfig
, ListAdapterConfig负责主线程和后台线程的调度以及DiffCallback
的管理,DiffCallback
的接口实现中定义比较的规则,比较的工作则是由PagedStorageDiffHelper
来完成。
上述关于Paging的介绍参考Android.Arch.Paging: 分页加载的新选项
Paging的使用
导入Paging库:
1234567891011 | dependencies { def paging_version = "1.0.0" implementation "android.arch.paging:runtime:$paging_version" // alternatively - without Android dependencies for testing testImplementation "android.arch.paging:common:$paging_version" // optional - RxJava support, currently in release candidate implementation "android.arch.paging:rxjava2:1.0.0-rc1"} |
接下来我们看看关于Paging的具体使用
1、创建DataRepository
首先创建DataRepository用于数据的加载,因为这里我们主要实验Paging的使用,所以这里我们就模拟一个本地数据就好了,代码如下:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152 | //数据类data class DataBean(var id:Long, var name:String)class DataRepository : AnkoLogger { private val data = ArrayList |
DataRepository初始化的时候我们对data初始化101条数据进去,然后提供两个loadData
和一个loadData
方法,第一个loadData用于初始加载数据,第二个是根据index加载数据,loadPageData是分页加载数据
2、自定义DataSource
123456789101112131415161718192021222324 | class CustomPageDataSource(val repository: DataRepository) : PageKeyedDataSource |
CustomItemDataSource
继承自PageKeyedDataSource
,实现了loadInitial
、loadAfter
、loadBefore
方法,
其中
loadInitial
初始加载数据loadAfter
向后分页加载数据loadBefore
向前分页加载数据
这三个方法都有两个参数,一个params
和一个callback
。
其中params包装了分页加载的参数,loadInitial
中的params为LoadInitialParams
包含了requestedLoadSize
和placeholdersEnabled
两个属性,requestedLoadSize为加载的数据量,placeholdersEnabled是是否显示占位及当数据为null时显示占位的view
loadBefore
和loadAfter
中的params为LoadParams
包含了key
和requestedLoadSize
,key即为DataSource
中的key,在这里即为页数
callback
为数据加载完成的回调,loadInitial
中调用调用DataRepository
加载数据,然后调用callback.onResult
告诉调用者数据加载完成
12 | public abstract void onResult(@NonNull List |
onResult
有三个参数,第一个为数据,后面两个即为上一页和下一页
如果我们当前页为第一页即没有上一页,则上一页为null,下一页为2,此时加载的时候会加载当前页和调用loadAfter
加载第二页,但不会调用loadBefore
因为没有上一页,即previousPageKey为null不会加载上一页
如果我们初始加载的是第三页,则上一页是2,下一页是4,此时加载的时候会加载当前页和调用loadAfter
加载第4页,调用loadBefore
加载第二页
分页加载的时候会将previousPageKey
或nextPageKey
传递到loadAfter
或loadBefore
中的params.key
loadAfter
、loadBefore
中的params中的key即为我们要加载页的数据,加载完后回调中告知下一次加载数据页数+1
或者-1
接下来创建DataSourceFactory类,创建PageList的使用用得着
12345 | class CustomPageDataSourceFactory(val repository: DataRepository) : DataSource.Factory |
很简单,不多说
3、创建PageList
Paging
库中提供了LivePagedListBuilder
用于创建PageList
,LivePagedListBuilder有两个构造方法:
1234 | public LivePagedListBuilder(@NonNull DataSource.Factory |
第一个参数为dataSourceFactory,即我们上面创建的CustomPageDataSourceFactory
,第二个参数一个是PagedList.Config
一个是pageSize
,pagesize好理解即分页加载每页的数据条数,PagedList.Config
我们前面介绍PagedList的时候也介绍过了。LivePagedListBuilder.build()
返回LiveData
一个LiveData包裹的PageList,便于观察数据更新刷新界面。
12345 | val data = LivePagedListBuilder(CustomPageDataSourceFactory(DataRepository()), PagedList.Config.Builder() .setPageSize(20) .setEnablePlaceholders(true) .setInitialLoadSizeHint(20) .build()).build() |
4、创建PageAdapter
12345678910111213141516171819202122232425262728293031323334 | class CustomAdapter : PagedListAdapter |
adapter继承PagedListAdapter
,它的构造函数需要一个DiffUtil.ItemCallback
,它的作用是判断两个item的数据是否相等
5、使用
前面我们准备工作都做完了,Paging的三大部分我们都实现了,接下来看看怎么使用:
1234567891011121314151617181920 | class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val adapter = CustomAdapter() recycle.adapter = adapter val data = LivePagedListBuilder(CustomPageDataSourceFactory(DataRepository()), PagedList.Config.Builder() .setPageSize(20) .setEnablePlaceholders(true) .setInitialLoadSizeHint(20) .build()).build() data.observe(this, Observer { adapter.submitList(it) }) }} |
布局里就一个RecyclerView就不贴代码了,因为data是一个LiveData
所以这里给它设置一个观察,当数据变动时调用adapter.submitList
刷新数据,submitList
是PagedListAdapter
的方法,它里面会去检查新数据是否跟旧数据是否相同,有兴趣的可以去看看源码。
看看效果:
日志:
1234567891011121314 | 05-29 17:09:56.870 17080-17119/? I/CustomPageDataSource: loadInitial size : 20 05-29 17:09:56.870 17080-17119/? I/DataRepository: init load 2005-29 17:09:56.912 17080-17119/? I/CustomPageDataSource: loadAfter size : 20 page:205-29 17:09:56.912 17080-17119/? I/DataRepository: loadPageData 2 2005-29 17:09:58.049 17080-17136/? I/CustomPageDataSource: loadAfter size : 20 page:305-29 17:09:58.049 17080-17136/? I/DataRepository: loadPageData 3 2005-29 17:09:58.454 17080-17119/? I/CustomPageDataSource: loadAfter size : 20 page:405-29 17:09:58.454 17080-17119/? I/DataRepository: loadPageData 4 2005-29 17:09:58.789 17080-17136/? I/CustomPageDataSource: loadAfter size : 20 page:505-29 17:09:58.789 17080-17136/? I/DataRepository: loadPageData 5 2005-29 17:09:59.106 17080-17119/? I/CustomPageDataSource: loadAfter size : 20 page:605-29 17:09:59.106 17080-17119/? I/DataRepository: loadPageData 6 2005-29 17:09:59.140 17080-17136/? I/CustomPageDataSource: loadAfter size : 20 page:705-29 17:09:59.140 17080-17136/? I/DataRepository: loadPageData 7 20 |
说明确实是分页加载的数据。
上面我们用的是继承PageKeyedDataSource
,继承ItemKeyedDataSource
其实也差不多:
1234567891011121314151617181920212223242526272829 | class CustomItemDataSource(val repository: DataRepository) : ItemKeyedDataSource |
比PageKeyedDataSource
多了一个getKey
方法,因为ItemKeyedDataSource
根据item的信息分页加载数据的,比如这里使用排序id去加载,原理一样只是加载数据的方式不一样。
好了Paging的初探就到这里了,更多关于Paging的介绍请看官方文档:Paging library
更多相关文章
- “罗永浩抖音首秀”销售数据的可视化大屏是怎么做出来的呢?
- Nginx系列教程(三)| 一文带你读懂Nginx的负载均衡
- 不吹不黑!GitHub 上帮助人们学习编码的 12 个资源,错过血亏...
- Android(安卓)Intent的几个主要用法--发短信,打电话,发彩信
- android ViewPager,ViewFlipper,ViewFlow实现左右滑动
- android 使用SQLite对数据进行增删改查、访问
- Android获取未安装apk
- Android中statfs使用注意事项
- Android(安卓)MediaProvider数据库模式