閱讀本文之前,可先閱讀同系列 Android Jetpack 組件文章如下:
- Android Jetpack 組件之 Lifecycle 篇
- Android Jetpack 組件之 LiveData 篇
- Android Jetpack 組件之 ViewModel 篇
- Android Jetpack 組件之 DataBinding 篇
- Android Jetpack 組件之 BindingAdapter 篇
- Android Jetpack 組件之使用可觀察的數據對象
本文將介紹 Paging Library 庫的使用,其源碼解析將在下篇文章中介紹,Paging Library 組件是 Android Jetpack 的一部分,是 Google 推出的官方分頁組件,如果項目中使用了 Google 新推出的官方架構組件,如 LiveData、Lifecycle、ViewModel 等,就可以嘗試將該分頁組件引入自己的項目,它的優點是無痕加載更多數據,一定程度上提高用戶體驗。
簡述一下使用 paging 組件分頁加載數據的過程,DataSource 負責從網絡或數據庫加載數據,將數據存儲在 PagedList 中,使用 submitList 提交數據到 PagedListAdapter,當數據發生變化時會在後台線程中計算數據差異,最後 PagedListAdapter 通知 RecyclerView 更新數據。
- 準備數據
- 引入 Paging Library 組件
- 自定義 DataSource
- 配置分頁參數
- 加載顯示數據
- 測試效果
- Paging Library 源碼解析
準備數據#
這裡使用幹貨集中營的開源 Api 來進行測試,具體如下:
public interface CommonAPI {
// 這裡使用幹貨集中營開源API:http://gank.io/api/search/query/listview/category/Android/count/10/page/1
@GET("api/search/query/listview/category/Android/count/8/page/{page}")
Call<List<DataBean.ResultsBean>> getArticleList1(@Path("page") int page);
}
引入 Paging Library 組件#
引入 Paging Library 庫如下:
def paging_version = "2.1.0"
// androidx
implementation "androidx.paging:paging-runtime:$paging_version"
// 老版本 (page library 2.0.0-rc01)
implementation "android.arch.paging:runtime:$paging_version"
這裡使用的是 androidx 最新版本。
自定義 DataSource#
自定義 DataSource 加載數據,這裡加載的網絡數據,使用 PageKeyedDataSource 更合適,繼承 PageKeyedDataSource 自定義 DataSource 如下:
// 自定義DataSource
public class MDataSource extends PageKeyedDataSource<String, DataBean.ResultsBean> {
private static final String TAG = MDataSource.class.getSimpleName();
private int mPage = 1;
public MDataSource() {
}
// 初始化
@Override
public void loadInitial(@NonNull LoadInitialParams<String> params,
@NonNull final LoadInitialCallback<String, DataBean.ResultsBean> callback) {
Log.i(TAG, "--loadInitial-->");
CommonAPI api = RetrofitApi.getInstance().mRetrofit.create(CommonAPI.class);
Call<List<DataBean.ResultsBean>> call = api.getArticleList1(mPage);
call.enqueue(new Callback<List<DataBean.ResultsBean>>() {
@Override
public void onResponse(Call<List<DataBean.ResultsBean>> call, Response<List<DataBean.ResultsBean>> response) {
Log.i(TAG, "--onResponse-->" + response.toString());
if (response.isSuccessful() && response.code() == 200) {
List<DataBean.ResultsBean> data = response.body();
callback.onResult(data, "before", "after");
}
}
@Override
public void onFailure(Call<List<DataBean.ResultsBean>> call, Throwable t) {
Log.i(TAG, "--onFailure-->" + t.getMessage());
}
});
}
// 加載上一頁
@Override
public void loadBefore(@NonNull LoadParams<String> params,
@NonNull LoadCallback<String, DataBean.ResultsBean> callback) {
Log.i(TAG, "--loadBefore-->" + params.key);
}
// 加載下一頁
@Override
public void loadAfter(@NonNull final LoadParams<String> params,
@NonNull final LoadCallback<String, DataBean.ResultsBean> callback) {
Log.i(TAG, "--loadAfter-->" + params.key);
mPage++;
CommonAPI api = RetrofitApi.getInstance().mRetrofit.create(CommonAPI.class);
Call<List<DataBean.ResultsBean>> call = api.getArticleList1(mPage);
call.enqueue(new Callback<List<DataBean.ResultsBean>>() {
@Override
public void onResponse(Call<List<DataBean.ResultsBean>> call, Response<List<DataBean.ResultsBean>> response) {
Log.i(TAG, "--onResponse-->" + response.toString());
if (response.isSuccessful() && response.code() == 200) {
List<DataBean.ResultsBean> data = response.body();
callback.onResult(data, params.key);
}
}
@Override
public void onFailure(Call<List<DataBean.ResultsBean>> call, Throwable t) {
Log.i(TAG, "--onFailure-->" + t.getMessage());
}
});
}
}
很簡單沒有什麼多餘的東西,細節可以參考後文中的源碼解析,創建一個工廠方便數據變化是創建新的 DataSource 如下:
// MDataSource創建工廠
public class MDataSourceFactory extends DataSource.Factory<String, DataBean.ResultsBean> {
public MDataSourceFactory() {
}
@NonNull
@Override
public DataSource<String, DataBean.ResultsBean> create() {
MDataSource mDataSource = new MDataSource();
return mDataSource;
}
}
配置分頁參數#
在 ViewModel 中創建 PagedList.Config 並進行分頁參數配置,創建 DataSource 工廠對象,最終生成支持分頁的 LiveData 數據,具體參考如下:
// ViewModel
public class MViewModel extends ViewModel {
private int pageSize = 20;
// PagedList配置
private PagedList.Config config = new PagedList.Config.Builder()
.setInitialLoadSizeHint(pageSize)//設置首次加載的數量
.setPageSize(pageSize)//設置每頁加載的數量
.setPrefetchDistance(2)//設置距離每頁最後數據項來時預加載下一頁數據
.setEnablePlaceholders(false)//設置是否啟用UI佔用符
.build();
// DataSource.Factory
private DataSource.Factory<String,DataBean.ResultsBean> factory = new MDataSourceFactory();
// LiveData
private LiveData<PagedList<DataBean.ResultsBean>> mPagedList = new LivePagedListBuilder<>(factory, config)
.build();
public LiveData<PagedList<DataBean.ResultsBean>> getPagedList() {
return mPagedList;
}
}
加載顯示數據#
這裡使用 LiveData 監聽加載的數據,然後使用 submitList 將數據提交給 PagedListAdapter,會在後台線程中對比新舊數據的差異,最後更新 RecyclerView,具體如下:
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private RecyclerView mRecyclerView;
private ArticleAdapter mAdapter;
private MViewModel mViewModel;
private static DiffUtil.ItemCallback<DataBean.ResultsBean> itemCallback = new DiffUtil.ItemCallback<DataBean.ResultsBean>() {
@Override
public boolean areItemsTheSame(@NonNull DataBean.ResultsBean oldItem, @NonNull DataBean.ResultsBean newItem) {
return oldItem.getGanhuo_id() == newItem.getGanhuo_id();
}
@Override
public boolean areContentsTheSame(@NonNull DataBean.ResultsBean oldItem, @NonNull DataBean.ResultsBean newItem) {
return oldItem.equals(newItem);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = findViewById(R.id.rvData);
mAdapter = new ArticleAdapter(itemCallback);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setAdapter(mAdapter);
ViewModelProvider mViewModelProvider = new ViewModelProvider(this,
new ViewModelProvider.AndroidViewModelFactory(getApplication()));
mViewModel = mViewModelProvider.get(MViewModel.class);
}
public void getData(View view) {
mViewModel.getPagedList().observe(this, new Observer<PagedList<DataBean.ResultsBean>>() {
@Override
public void onChanged(PagedList<DataBean.ResultsBean> dataBeans) {
Log.i(TAG, "--onChanged-->");
mAdapter.submitList(dataBeans);
}
});
}
}
測試效果#
以上就是 Page Library 庫的使用方式。
Paging Library 源碼解析#
接著 LiveData 的 observer 方法進入開始,源碼如下:
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
// 記住這個LifecycleBoundObserver,後面有用
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
// 添加Observer,具體是LifecycleBoundObserver
owner.getLifecycle().addObserver(wrapper);
}
繼續查看 LifecycleRegistry 的 addObserver 方法,源碼如下:
@Override
public void addObserver(@NonNull LifecycleObserver observer) {
// ...
ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
while ((statefulObserver.mState.compareTo(targetState) < 0
&& mObserverMap.contains(observer))) {
pushParentState(statefulObserver.mState);
// ObserverWithState的dispatchEvent方法
statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState));
popParentState();
// mState / subling may have been changed recalculate
targetState = calculateTargetState(observer);
}
// ...
}
static class ObserverWithState {
State mState;
LifecycleEventObserver mLifecycleObserver;
ObserverWithState(LifecycleObserver observer, State initialState) {
mLifecycleObserver = Lifecycling.lifecycleEventObserver(observer);
mState = initialState;
}
void dispatchEvent(LifecycleOwner owner, Event event) {
State newState = getStateAfter(event);
mState = min(mState, newState);
// 實際調用的是LifecycleBoundObserver的onStateChanged方法
mLifecycleObserver.onStateChanged(owner, event);
mState = newState;
}
}
繼續查看 LiveData 的內部類 LifecycleBoundObserver 的 onStateChanged 方法,源碼如下:
// 方法
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
// Lifecycle組件監聽的生命週期方法回調
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
// 查看源碼shouldBeActive方法知,只要Activity的狀態在ON_START之後就返回true
activeStateChanged(shouldBeActive());
}
// activeStateChanged方法
void activeStateChanged(boolean newActive) {
// mActive默認false,所以不成立
// 如果第二次執行mActive為false,則不繼續處理,最終的結果就是不會回到onChanged方法
if (newActive == mActive) {
return;
}
// 第一次執行完後mActive的值將被設置為true
mActive = newActive;
// 只要有活躍觀察者,也就是組件的狀態是START或RESUME時mActiveCount就不等於0,wasInactive為true
LiveData.this.mActiveCount += mActive ? 1 : -1;
// wasInactive和mActive都為true,執行
if (wasInactive && mActive) {
onActive();
}
// mActiveCount大於0 ,fasle && false,不執行
if (LiveData.this.mActiveCount == 0 && !mActive) {
onInactive();
}
// mActive為true
if (mActive) {
//分發處理ObserverWrapper,也就是添加進去的觀察者LifecycleBoundObserver
dispatchingValue(this);
}
}
看到這裡至少就可以解釋為什麼在 PagedList 初始加載數據時會回調 onChanged 方法,而在加載下一頁數據時不再回調 onChanged 方法了,
繼續查看 LiveData 的方法 dispatchingValue 方法,源碼如下:
// dispatchingValue方法
void dispatchingValue(@Nullable ObserverWrapper initiator) {
// ...
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
// 遍歷獲取Observer處理
considerNotify(iterator.next().getValue());
//...
}
// ...
}
// considerNotify方法
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
return;
}
// 觀察者不處於活躍狀態,不通知觀察者
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
// 通知觀察者
observer.mObserver.onChanged((T) mData);
}
至此,將結果回調給具體的觀察者。
從 mPagedList 的創建開始,創建 mPagedList 代碼如下:
// LiveData
private LiveData<PagedList<DataBean.ResultsBean>> mPagedList = new LivePagedListBuilder<>(factory,config)
.build();
繼續查看關鍵方法 build,源碼如下:
// build方法
public LiveData<PagedList<Value>> build() {
return create(mInitialLoadKey, mConfig, mBoundaryCallback, mDataSourceFactory,
ArchTaskExecutor.getMainThreadExecutor(), mFetchExecutor);
}
// create方法
private static <Key, Value> LiveData<PagedList<Value>> create(
return new ComputableLiveData<PagedList<Value>>(fetchExecutor) {
@Nullable
private PagedList<Value> mList;
@Nullable
private DataSource<Key, Value> mDataSource;
// DataSource失效回調
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);
// 創建PagedList,PageList是如何創建存儲的且看下文
mList = new PagedList.Builder<>(mDataSource, config)
.setNotifyExecutor(notifyExecutor)
.setFetchExecutor(fetchExecutor)
.setBoundaryCallback(boundaryCallback)
.setInitialKey(initializeKey)
.build();
} while (mList.isDetached());//DataSource無效時,還是使用以前的數據,DataSource有效是執行一次返回PagedList
return mList;
}
}.getLiveData();
}
使用 getLiveData 獲取 LiveData 數據,其中 LiveData 數據的更新是在一個名為 mRefreshRunnable 的 Runnable 中更新的,在 mRefreshRunnable 中會調用上面的 compute 方法生成 PagedList,然後使用 LiveData 的 postValue 方法更新 PageList 數據到 LiveData 中,源碼如下:
final Runnable mRefreshRunnable = new Runnable() {
// ...
try {
T value = null;
while (mInvalid.compareAndSet(true, false)) {
computed = true;
// 生成PagedList
value = compute();
}
if (computed) {
// 更新LiveData
mLiveData.postValue(value);
}
} finally {
// release compute lock
mComputing.set(false);
}
// ...
};
到此為止,LiveData<PagedList> 從創建到更新就分析完了。
接著上文繼續看一下 PagedList 是如何生成的,PagedList 創建的關鍵源碼如下:
// PagedList的創建
mList = new PagedList.Builder<>(mDataSource, config)
.setNotifyExecutor(notifyExecutor)
.setFetchExecutor(fetchExecutor)
.setBoundaryCallback(boundaryCallback)
.setInitialKey(initializeKey)
.build();
// 續查看build方法,調用了PagedList的create方法
public PagedList<Value> build() {
// ...
return PagedList.create(
mDataSource,
mNotifyExecutor,
mFetchExecutor,
mBoundaryCallback,
mConfig,
mInitialKey);
}
// 真正創建PagedList
static <K, T> PagedList<T> create(@NonNull DataSource<K, T> dataSource,
@NonNull Executor notifyExecutor,
@NonNull Executor fetchExecutor,
@Nullable BoundaryCallback<T> boundaryCallback,
@NonNull Config config,
@Nullable K key) {
// PageKeyedDataSource繼承ContiguousDataSource,dataSource.isContiguous()為true
if (dataSource.isContiguous() || !config.enablePlaceholders) {
int lastLoad = ContiguousPagedList.LAST_LOAD_UNSPECIFIED;
// 使用PageKeyedDataSource不會執行
if (!dataSource.isContiguous()) {
//noinspection unchecked
dataSource = (DataSource<K, T>) ((PositionalDataSource<T>) dataSource)
.wrapAsContiguousWithoutPlaceholders();
if (key != null) {
lastLoad = (Integer) key;
}
}
// 創建並返回ContiguousPagedList,也就是PagedList
ContiguousDataSource<K, T> contigDataSource = (ContiguousDataSource<K, T>) dataSource;
return new ContiguousPagedList<>(contigDataSource,
notifyExecutor,
fetchExecutor,
boundaryCallback,
config,
key,
lastLoad);
} else {
return new TiledPagedList<>((PositionalDataSource<T>) dataSource,
notifyExecutor,
fetchExecutor,
boundaryCallback,
config,
(key != null) ? (Integer) key : 0);
}
}
繼續查看 ContiguousPagedList 的創建,其關鍵源碼如下:
ContiguousPagedList(
@NonNull ContiguousDataSource<K, V> dataSource,
@NonNull Executor mainThreadExecutor,
@NonNull Executor backgroundThreadExecutor,
@Nullable BoundaryCallback<V> boundaryCallback,
@NonNull Config config,
final @Nullable K key,
int lastLoad) {
super(new PagedStorage<V>(), mainThreadExecutor, backgroundThreadExecutor,
boundaryCallback, config);
// ...
mDataSource.dispatchLoadInitial(key,
mConfig.initialLoadSizeHint,
mConfig.pageSize,
mConfig.enablePlaceholders,
mMainThreadExecutor,
mReceiver);
// ...
}
這裡調用了抽象類 ContiguousDataSource 的 dispatchLoadInitial 方法,查看各類具體的實現類 PageKeyedDataSource 裡面實現的具體的 dispatchLoadInitial 方法,源碼如下:
@Override
final void dispatchLoadInitial(@Nullable Key key, int initialLoadSize, int pageSize,
boolean enablePlaceholders, @NonNull Executor mainThreadExecutor,
@NonNull PageResult.Receiver<Value> receiver) {
// 創建了LoadInitialCallback,也就是自定義DataSource中回調從網絡加載數據時使用的callback
LoadInitialCallbackImpl<Key, Value> callback =
new LoadInitialCallbackImpl<>(this, enablePlaceholders, receiver);
// 自定義DataSource需要實現的loadInitial方法,在這裡完成回調
loadInitial(new LoadInitialParams<Key>(initialLoadSize, enablePlaceholders), callback);
// 設置callback的回調執行在主線程
callback.mCallbackHelper.setPostExecutor(mainThreadExecutor);
}
到此為止,還不知道回調過去的數據時怎麼保存的,所以繼續查看 callback 的具體實現及回調,源碼如下:
static class LoadInitialCallbackImpl<Key, Value> extends LoadInitialCallback<Key, Value> {
final LoadCallbackHelper<Value> mCallbackHelper;
private final PageKeyedDataSource<Key, Value> mDataSource;
private final boolean mCountingEnabled;
// LoadInitialCallbackImpl構造方法
LoadInitialCallbackImpl(@NonNull PageKeyedDataSource<Key, Value> dataSource,
boolean countingEnabled, @NonNull PageResult.Receiver<Value> receiver) {
// 這裡需記住ResultType為PageResult.INIT,也就是初始加載數據時候數據的類型,後文中遇到PageResult.INIT
mCallbackHelper = new LoadCallbackHelper<>(
dataSource, PageResult.INIT, null, receiver);
mDataSource = dataSource;
mCountingEnabled = countingEnabled;
}
// 這個onResult回調顯然不是PageKeyedDataSource設置數據時使用,應該是使用PositionalDataSource使用的,這裡略過
@Override
public void onResult(@NonNull List<Value> data, int position, int totalCount,
@Nullable Key previousPageKey, @Nullable Key nextPageKey) {
if (!mCallbackHelper.dispatchInvalidResultIfInvalid()) {
LoadCallbackHelper.validateInitialLoadParams(data, position, totalCount);
// setup keys before dispatching data, so guaranteed to be ready
mDataSource.initKeys(previousPageKey, nextPageKey);
int trailingUnloadedCount = totalCount - position - data.size();
if (mCountingEnabled) {
mCallbackHelper.dispatchResultToReceiver(new PageResult<>(
data, position, trailingUnloadedCount, 0));
} else {
mCallbackHelper.dispatchResultToReceiver(new PageResult<>(data, position));
}
}
}
// 這個onResult回調是PageKeyedDataSource設置數據時使用的
@Override
public void onResult(@NonNull List<Value> data, @Nullable Key previousPageKey,
@Nullable Key nextPageKey) {
// DataSource有效時dispatchInvalidResultIfInvalid返回false
if (!mCallbackHelper.dispatchInvalidResultIfInvalid()) {
// 初始化在loadInitial中設置的previousPageKey, nextPageKey,應該是用來區分加載上一頁還是加載下一頁
mDataSource.initKeys(previousPageKey, nextPageKey);
// 分發回調過來的數據data
mCallbackHelper.dispatchResultToReceiver(new PageResult<>(data, 0, 0, 0));
}
}
}
這裡已經拿到了從網絡加載的數據,繼續查看 dispatchResultToReceiver 方法,看數據時如何被處理的,其源碼如下:
void dispatchResultToReceiver(final @NonNull PageResult<T> result) {
Executor executor;
synchronized (mSignalLock) {
if (mHasSignalled) {
throw new IllegalStateException(
"callback.onResult already called, cannot call again.");
}
mHasSignalled = true;
executor = mPostExecutor;
}
// 如果executor為null,因為在上面的分析中,這裡的回調executor被賦值並運行在主線程
if (executor != null) {
executor.execute(new Runnable() {
@Override
public void run() {
// 數據繼續回調
mReceiver.onPageResult(mResultType, result);
}
});
} else {// 非主線程直接回調
mReceiver.onPageResult(mResultType, result);
}
}
這裡繼續查看 onPageResult 的具體實現,查看源碼知這個過程在 ContiguousPagedList 中實現,其源碼具體如下:
PageResult.Receiver<V> mReceiver = new PageResult.Receiver<V>() {
@AnyThread
@Override
public void onPageResult(@PageResult.ResultType int resultType,
@NonNull PageResult<V> pageResult) {
// ...
List<V> page = pageResult.page;
// 前面在初始化加載數據,創建LoadCallbackHelper時,數據狀態是PageResult.INIT
// 也就是初始化調用loadInitial的時候
if (resultType == PageResult.INIT) {
// 1.保存數據到PagedStorage
// 2.最終調用RecyclerView.Adapte的notifyItemRangeInserted(position, count)方法,position為0
mStorage.init(pageResult.leadingNulls, page, pageResult.trailingNulls,
pageResult.positionOffset, ContiguousPagedList.this);
// ...
} else {
// ...
// 創建LoadCallbackHelper時,數據狀態是PageResult.APPEND
// 也就是調用loadAfter的時候
if (resultType == PageResult.APPEND) {
if (skipNewPage && !trimFromFront) {
// don't append this data, drop it
mAppendItemsRequested = 0;
mAppendWorkerState = READY_TO_FETCH;
} else {
// 1.保存數據到PagedStorage
// 2.最終回調loadAfter
mStorage.appendPage(page, ContiguousPagedList.this);
}
// 創建LoadCallbackHelper時,數據狀態是PageResult.PREPEND
// 也就是調用loadBefore的時候
} else if (resultType == PageResult.PREPEND) {
if (skipNewPage && trimFromFront) {
// don't append this data, drop it
mPrependItemsRequested = 0;
mPrependWorkerState = READY_TO_FETCH;
} else {
// 1.保存數據到PagedStorage
// 2.最終回調loadBefore
mStorage.prependPage(page, ContiguousPagedList.this);
}
} else {
throw new IllegalArgumentException("unexpected resultType " + resultType);
}
// ...
}
};
到此 PagedList 的創建以及保存過程就結束了,之前從 observer 開始分析了 PagedList 的數據回調,會調用 onChanged 方法,在該方法裡面為 RecyclerVIew 的 Adaptyer 設置數據,代碼如下:
mViewModel.getPagedList().observe(this, new Observer<PagedList<DataBean.ResultsBean>>() {
@Override
public void onChanged(PagedList<DataBean.ResultsBean> dataBeans) {
// 設置要顯示的數據,只會在初始化的時候回調一次
// 如果已經有顯示列表,則會在後台線程中計算數據差異,並通知數據更新
mAdapter.submitList(dataBeans);
}
});
上面提到如果已經存在顯示列表,則會在後台線程中計算數據差異並通知數據更新,那麼這個後台線程在哪呢,mAdapter 的 submitList 方法最終調用下面的 submitList 方法,裡面通過 AsyncDifferConfig 獲取後台線程執行數據的差異計算,這個後台線程池如果不指定則是一個固定大小為 2 的線程池,源碼如下:
@SuppressWarnings("ReferenceEquality")
public void submitList(@Nullable final PagedList<T> pagedList,
@Nullable final Runnable commitCallback) {
// ...
final PagedList<T> oldSnapshot = mSnapshot;
final PagedList<T> newSnapshot = (PagedList<T>) pagedList.snapshot();
// 獲取後台線程池執行計算數據差異的線程,並在裡面將計算結果回調到主線程
mConfig.getBackgroundThreadExecutor().execute(new Runnable() {
@Override
public void run() {
final DiffUtil.DiffResult result;
result = PagedStorageDiffHelper.computeDiff(
oldSnapshot.mStorage,
newSnapshot.mStorage,
mConfig.getDiffCallback());
// 將計算結果回調到主線程,然後通知數據集更新,進而通知RecyclerView更新數據
mMainThreadExecutor.execute(new Runnable() {
@Override
public void run() {
if (mMaxScheduledGeneration == runGeneration) {
latchPagedList(pagedList, newSnapshot, result,
oldSnapshot.mLastLoad, commitCallback);
}
}
});
}
});
}
繼續查看 latchPagedList 源碼,這裡將會進行數據的更新操作以及讀取數據,其源碼如下:
void latchPagedList(
@NonNull PagedList<T> newList,
@NonNull PagedList<T> diffSnapshot,
@NonNull DiffUtil.DiffResult diffResult,
int lastAccessIndex,
@Nullable Runnable commitCallback) {
// ...
// 更新mPageList並通知RecyclerView更新數據
PagedStorageDiffHelper.dispatchDiff(mUpdateCallback,
previousSnapshot.mStorage, newList.mStorage, diffResult);
newList.addWeakCallback(diffSnapshot, mPagedListCallback);
if (!mPagedList.isEmpty()) {
// 讀取數據
mPagedList.loadAround(Math.max(0, Math.min(mPagedList.size() - 1, newPosition)));
}
onCurrentListChanged(previousSnapshot, mPagedList, commitCallback);
}
其中 dispatchDiff 方法最終會調用 dispatchLastEvent 方法,源碼如下:
public void dispatchLastEvent() {
if (mLastEventType == TYPE_NONE) {
return;
}
switch (mLastEventType) {
case TYPE_ADD:
mWrapped.onInserted(mLastEventPosition, mLastEventCount);
break;
case TYPE_REMOVE:
mWrapped.onRemoved(mLastEventPosition, mLastEventCount);
break;
case TYPE_CHANGE:
mWrapped.onChanged(mLastEventPosition, mLastEventCount, mLastEventPayload);
break;
}
mLastEventPayload = null;
mLastEventType = TYPE_NONE;
}
mWrapped 是一個 ListUpdateCallback,而 AdapterListUpdateCallback 實現了該接口,並在裡面通知 RectclerView 的 Adapter 進行數據更新。
其中在 latchPagedList 方法中會調用 PagedList 的 loadAround 方法來獲取數據,也就是使用在使用 PageKeyedDataSource 的時候實現的 loadBefore 和 loadAfter 方法,這裡就不貼源碼了。
Page Library 的分頁加載流程就分析完了,看源碼至少知道了 onChanged 方法只在初始化的時候只回調過一次,但是卻能夠不斷加載下一頁數據的原因。