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() 方法就會死循環。