Jetpack 的一个分页库,帮助开发者更好的分离ui和数据获取的逻辑,下降项目的耦合。本文主要描述从服务器直接获取数据git
项目地址:github.com/Tkorn/kotli…github
fun createDataSourceFactory(dataSource: DataSource<Long, UserBean>):DataSource.Factory<Long, UserBean>{ return object: DataSource.Factory<Long,UserBean>(){ override fun create(): DataSource<Long, UserBean> { return dataSource } } } 复制代码
DataSource, Paging已经帮咱们提供了三个很是全面的实现,分别是:bash
这里咱们只介绍一下 ItemKeyedDataSource 的使用。 ItemKeyedDataSource 一样是一个抽象类,须要咱们实现如下四个方法:服务器
PagedList, 分页库的关键组件是 PagedList 类,用于加载应用数据块或页面。随着所需数据的增多,系统会将其分页到现有的 PagedList 对象中。若是任何已加载的数据发生更改,会从 LiveData 或基于 RxJava2 的对象向可观察数据存储器发出一个新的 PagedList 实例。随着 PagedList 对象的生成,应用界面会呈现其内容,同时还会考虑界面控件的生命周期。(官方翻译) 能够经过 LivePagedListBuilder 建立markdown
val userLiveData =
LivePagedListBuilder(mRepository.createDataSourceFactory(createDataSource()),
mRepository.createConfig())
.setInitialLoadKey(1)
.build()
复制代码
LivePagedListBuilder(DataSource.Factory<Key, Value> dataSourceFactory, PagedList.Config config)架构
LivePagedListBuilder 须要DataSource.Factory(上面介绍了)、PagedList.Config(提供分页须要的参数)mvvm
Config( int pageSize, //一页加载多少数据 int prefetchDistance,//加载到第几条时请求下一页数据 boolean enablePlaceholders, 是否使用占位符 int initialLoadSizeHint, //第一次加载多少数据 int maxSize //最多保存多少数据 ){...} 复制代码
class PagingAdapter : PagedListAdapter<ArticleModel, PagingVH>(diffCallbacks) { companion object { private val diffCallbacks = object : DiffUtil.ItemCallback<ArticleModel>() { override fun areItemsTheSame(oldItem: ArticleModel, newItem: ArticleModel): Boolean = oldItem.id == newItem.id override fun areContentsTheSame(oldItem: ArticleModel, newItem: ArticleModel): Boolean = oldItem == newItem } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PagingVH = PagingVH(R.layout.item_paging_article_layout, parent) override fun onBindViewHolder(holder: PagingVH, position: Int) = holder.bind(getItem(position)) } 复制代码
这样adapter也已经构建完成,最后一旦PagedList被观察到,使用submitList传入到adapter便可。ide
viewModel.userList.observe(this, Observer { adapter.submitList(it) }) 复制代码
详细使用能够看看个人Demo,使用了mvvm + LiveData + Koin + 协程 + Retrofit。项目地址在最上面oop
//源码 @AnyThread public void invalidate() { if (mInvalid.compareAndSet(false, true)) { for (InvalidatedCallback callback : mOnInvalidatedCallbacks) { callback.onInvalidated(); } } } 复制代码
LivePagedListBuilder 的create() 重写了 DataSource.InvalidatedCallbackfetch
//LivePagedListBuilder 源码 @AnyThread @NonNull @SuppressLint("RestrictedApi") private static <Key, Value> LiveData<PagedList<Value>> create( @Nullable final Key initialLoadKey, @NonNull final PagedList.Config config, @Nullable final PagedList.BoundaryCallback boundaryCallback, @NonNull final DataSource.Factory<Key, Value> dataSourceFactory, @NonNull final Executor notifyExecutor, @NonNull final Executor fetchExecutor) { return new ComputableLiveData<PagedList<Value>>(fetchExecutor) { @Nullable private PagedList<Value> mList; @Nullable private DataSource<Key, Value> mDataSource; private final DataSource.InvalidatedCallback mCallback = new DataSource.InvalidatedCallback() { @Override public void onInvalidated() { invalidate(); } }; @SuppressWarnings("unchecked") // for casting getLastKey to Key @Override protected PagedList<Value> compute() { @Nullable Key initializeKey = initialLoadKey; if (mList != null) { initializeKey = (Key) mList.getLastKey(); } do { if (mDataSource != null) { mDataSource.removeInvalidatedCallback(mCallback); } mDataSource = dataSourceFactory.create(); mDataSource.addInvalidatedCallback(mCallback); mList = new PagedList.Builder<>(mDataSource, config) .setNotifyExecutor(notifyExecutor) .setFetchExecutor(fetchExecutor) .setBoundaryCallback(boundaryCallback) .setInitialKey(initializeKey) .build(); } while (mList.isDetached()); return mList; } }.getLiveData(); } } 复制代码
能够看到当 mList.isDetached() = true 时就会进入死循环。它的默认值是为false 的。在do 里面 mDataSource = dataSourceFactory.create(); mDataSource是从新获取了的,而后经过 PagedList.Builder 去获取新的 mList 数据。
@WorkerThread TiledPagedList(@NonNull PositionalDataSource<T> dataSource, @NonNull Executor mainThreadExecutor, @NonNull Executor backgroundThreadExecutor, @Nullable BoundaryCallback<T> boundaryCallback, @NonNull Config config, int position) { super(new PagedStorage<T>(), mainThreadExecutor, backgroundThreadExecutor, boundaryCallback, config); mDataSource = dataSource; final int pageSize = mConfig.pageSize; mLastLoad = position; if (mDataSource.isInvalid()) { detach(); } else { final int firstLoadSize = (Math.max(mConfig.initialLoadSizeHint / pageSize, 2)) * pageSize; final int idealStart = position - firstLoadSize / 2; final int roundedPageStart = Math.max(0, idealStart / pageSize * pageSize); mDataSource.dispatchLoadInitial(true, roundedPageStart, firstLoadSize, pageSize, mMainThreadExecutor, mReceiver); } } 复制代码
获取新数据前是会判断 mDataSource.isInvalid() ,这时若是咱们从新的DataSource的create() 的方法返回的不是新对象,而是以前的对象,这个mDataSource已经调用invalidate() 所以就会走到 detach() 方法,不获取新数据。
//源码 @SuppressWarnings("WeakerAccess") public void detach() { mDetached.set(true); } 复制代码
能够看到 detach() 会把mDetached 这位true,这就会致使mList.isDetached() 获取的时候是true,LivePagedListBuilder 的 create() 方法就会死循环。