当前位置:   article > 正文

【安卓开发系列 -- APP】JetPack -- Paging_paging 3.1.1 submitlist

paging 3.1.1 submitlist

【安卓开发系列 -- APP】JetPack -- Paging

【1】Paging 概念

Paging 组件是 Google 新推出的分页组件,可以帮助开发者实现 RecyclerView 中分页预加载以达到无限滑动的效果;

Paging 工作原理示意图

Paging 中的关键元素

名称说明
DataSource 或 DataSource.Factory

数据源提供者,DataSource将数据转变成PagedList,DataSource.Factory则用来创建DataSource;

按照数据分页加载的方式可以分为3种DataSource:

ItemKeyedDataSource:基于cursor实现,数据容量可动态自增;

PageKeyedDataSource:基于页码实现,数据容量可动态自增;

PositionalDataSource:数据容量固定,基于index加载特定范围的数据;

PagedList驱动Paging从数据源加载数据,同时负责页面初始化数据和控制分页数据加载的时机与加载方式;
PagedListAdapter列表适配器,通过DiffUtil确定差异并定向更新列表数据;
LivePagedListBuilder用于生成LiveData<PagedList>
BoundaryCallback数据到达边界的回调

【2】Paging 基本使用示例

  1. // 构建PagedList.Config对象,用以声明以何种方式分页
  2. PagedList.Config config = new PagedList.Config.Builder()
  3. .setPageSize(10) // 每次分页加载条目数目
  4. .setInitialLoadSizeHint(12) // 初始化数据加载的条目数目
  5. .build();
  6. // 创建数据源工厂类,用来创建数据提供者
  7. DataSource.Factory factory = new DataSource.Factory() {
  8. @NonNull
  9. @Override
  10. public DataSource create() {
  11. if (dataSource == null || dataSource.isInvalid()) {
  12. dataSource = createDataSource();
  13. }
  14. return dataSource;
  15. }
  16. };
  17. // BoundaryCallback
  18. // Signals when a PagedList has reached the end of available data
  19. // PagedList 的数据加载到达边界的回调方法
  20. PagedList.BoundaryCallback<T> callback = new PagedList.BoundaryCallback<T>() {
  21. @Override
  22. public void onZeroItemsLoaded() {
  23. // 新提交的PagedList中没有数据
  24. boundaryPageData.postValue(false);
  25. }
  26. @Override
  27. public void onItemAtFrontLoaded(@NonNull T itemAtFront) {
  28. // 新提交的PagedList中第一条数据被加载到列表上
  29. boundaryPageData.postValue(true);
  30. }
  31. @Override
  32. public void onItemAtEndLoaded(@NonNull T itemAtEnd) {
  33. // 新提交的PagedList中最后一条数据被加载到列表上
  34. }
  35. };
  36. // 构建一个能够触发加载页面初始化数据的LiveData对象
  37. LiveData<PagedList<T>> pageData = new LivePagedListBuilder(factory, config)
  38. .setInitialLoadKey(0)
  39. .setBoundaryCallback(callback)
  40. .build();
  41. // 用前面构建出来的LiveData对象注册一个Observer观察者,便可以触发页面初始化数据的加载了
  42. PagingViewModel viewModel = new ViewModelProvider(this).get(PagingViewModel.class);
  43. // 触发初始化数据加载
  44. viewModel.getPageData().observe(this, new Observer<PagedList<String>>() {
  45. @Override
  46. public void onChanged(PagedList<String> pagedList) {
  47. pagingAdapter.submitList(pagedList);
  48. }
  49. });

【3】Paging + LiveData 关键代码分析

【3.0】Paging 的工作流程

【3.1】创建 LiveData 对象

  1. public final class LivePagedListBuilder<Key, Value> {
  2. @NonNull
  3. @SuppressLint("RestrictedApi")
  4. public LiveData<PagedList<Value>> build() {
  5. // 构造LiveData<PagedList<Value>>类型的实例
  6. return create(mInitialLoadKey, mConfig, mBoundaryCallback, mDataSourceFactory,
  7. ArchTaskExecutor.getMainThreadExecutor(), mFetchExecutor);
  8. }
  9. @AnyThread
  10. @NonNull
  11. @SuppressLint("RestrictedApi")
  12. private static <Key, Value> LiveData<PagedList<Value>> create(
  13. @Nullable final Key initialLoadKey,
  14. @NonNull final PagedList.Config config,
  15. @Nullable final PagedList.BoundaryCallback boundaryCallback,
  16. @NonNull final DataSource.Factory<Key, Value> dataSourceFactory,
  17. @NonNull final Executor notifyExecutor,
  18. @NonNull final Executor fetchExecutor) {
  19. // 该方法直接新建了一个ComputableLiveData并复写了其compute方法
  20. // 最终返回ComputableLiveData中的LiveData实例
  21. return new ComputableLiveData<PagedList<Value>>(fetchExecutor) {
  22. @Nullable
  23. private PagedList<Value> mList;
  24. @Nullable
  25. private DataSource<Key, Value> mDataSource;
  26. // 新建数据源无效事件回调实例并在回调方法中调用了ComputableLiveData的invalidate方法
  27. // 即触发了ComputableLiveData的compute方法
  28. // Invalidation callback for DataSource
  29. //
  30. // mCallback被添加到DataSource类的mOnInvalidatedCallbacks成员变量中
  31. // private CopyOnWriteArrayList<InvalidatedCallback>
  32. // mOnInvalidatedCallbacks = new CopyOnWriteArrayList<>();
  33. // 在DataSource类的invalidate方法中遍历mOnInvalidatedCallbacks中保存的回调实例并触发其回调方法
  34. // public void invalidate()
  35. private final DataSource.InvalidatedCallback mCallback =
  36. new DataSource.InvalidatedCallback() {
  37. @Override
  38. public void onInvalidated() {
  39. invalidate();
  40. }
  41. };
  42. @SuppressWarnings("unchecked") // for casting getLastKey to Key
  43. @Override
  44. protected PagedList<Value> compute() {
  45. // 初始化initializeKey,该值要么由用户指定,要么为PagedList中最新的值
  46. @Nullable Key initializeKey = initialLoadKey;
  47. if (mList != null) {
  48. initializeKey = (Key) mList.getLastKey();
  49. }
  50. do {
  51. // 删除原有的DataSource中的InvalidatedCallback回调实例
  52. if (mDataSource != null) {
  53. mDataSource.removeInvalidatedCallback(mCallback);
  54. }
  55. // 重新创建DataSource
  56. mDataSource = dataSourceFactory.create();
  57. // 添加InvalidatedCallback回调用以监听该DataSource被置为无效的事件
  58. // 一旦DataSource被置为无效,则不能在提供数据,
  59. // 但会再次触发该方法(compute)再次创建一个新的mDataSource
  60. mDataSource.addInvalidatedCallback(mCallback);
  61. // 新建PagedList实例
  62. mList = new PagedList.Builder<>(mDataSource, config)
  63. .setNotifyExecutor(notifyExecutor)
  64. .setFetchExecutor(fetchExecutor)
  65. .setBoundaryCallback(boundaryCallback)
  66. .setInitialKey(initializeKey)
  67. .build();
  68. } while (mList.isDetached());
  69. return mList;
  70. }
  71. }.getLiveData();
  72. }
  73. }

【3.2】List UI 显示刷新回调实例的注册

创建了 LiveData 对象并将其与宿主建立观察关系之后,会触发一次 Observer 的 onChanged 回调,在该回调中调用 public void submitList(@Nullable PagedList<T> pagedList);方法设置新的待显示的列表并在此过程中添加了针对该列表显示相关的回调实例;

  1. public abstract class PagedListAdapter<T, VH extends RecyclerView.ViewHolder>
  2. extends RecyclerView.Adapter<VH> {
  3. final AsyncPagedListDiffer<T> mDiffer;
  4. public void submitList(@Nullable PagedList<T> pagedList) {
  5. // 调用 AsyncPagedListDiffer 类实例的 submitList 方法
  6. // 目的可以实现列表数据差异增量更新
  7. mDiffer.submitList(pagedList);
  8. }
  9. }
  10. public class AsyncPagedListDiffer<T> {
  11. public AsyncPagedListDiffer(@NonNull RecyclerView.Adapter adapter,
  12. @NonNull DiffUtil.ItemCallback<T> diffCallback) {
  13. // 新建 mUpdateCallback 实例
  14. mUpdateCallback = new AdapterListUpdateCallback(adapter);
  15. mConfig = new AsyncDifferConfig.Builder<>(diffCallback).build();
  16. }
  17. // mPagedListCallback 的相关回调方法将回调委托给 mUpdateCallback 相关回调处理
  18. // 从而实现对 RecyclerView 的具体 Adapter 的通知
  19. private PagedList.Callback mPagedListCallback = new PagedList.Callback() {
  20. @Override
  21. public void onInserted(int position, int count) {
  22. mUpdateCallback.onInserted(position, count);
  23. }
  24. @Override
  25. public void onRemoved(int position, int count) {
  26. mUpdateCallback.onRemoved(position, count);
  27. }
  28. @Override
  29. public void onChanged(int position, int count) {
  30. // NOTE: pass a null payload to convey null -> item
  31. mUpdateCallback.onChanged(position, count, null);
  32. }
  33. };
  34. public void submitList(@Nullable final PagedList<T> pagedList,
  35. @Nullable final Runnable commitCallback) {
  36. // 添加 PagedList.Callback 实例 mPagedListCallback
  37. pagedList.addWeakCallback(null, mPagedListCallback);
  38. }
  39. }
  40. public final class AdapterListUpdateCallback implements ListUpdateCallback {
  41. @NonNull
  42. private final RecyclerView.Adapter mAdapter;
  43. /**
  44. * Creates an AdapterListUpdateCallback that will dispatch update events to the given adapter.
  45. *
  46. * @param adapter The Adapter to send updates to.
  47. */
  48. public AdapterListUpdateCallback(@NonNull RecyclerView.Adapter adapter) {
  49. mAdapter = adapter;
  50. }
  51. /** {@inheritDoc} */
  52. // 通知 RecyclerView 的 Adapter 有 Item 插入
  53. @Override
  54. public void onInserted(int position, int count) {
  55. mAdapter.notifyItemRangeInserted(position, count);
  56. }
  57. /** {@inheritDoc} */
  58. // 通知 RecyclerView 的 Adapter 有 Item 移除
  59. @Override
  60. public void onRemoved(int position, int count) {
  61. mAdapter.notifyItemRangeRemoved(position, count);
  62. }
  63. /** {@inheritDoc} */
  64. // 通知 RecyclerView 的 Adapter 有 Item 移动
  65. @Override
  66. public void onMoved(int fromPosition, int toPosition) {
  67. mAdapter.notifyItemMoved(fromPosition, toPosition);
  68. }
  69. /** {@inheritDoc} */
  70. // 通知 RecyclerView 的 Adapter 有 Item 改变
  71. @Override
  72. public void onChanged(int position, int count, Object payload) {
  73. mAdapter.notifyItemRangeChanged(position, count, payload);
  74. }
  75. }

【3.3】触发 Paging 的初始化数据的加载

  1. class ContiguousPagedList<K, V> extends PagedList<V> implements PagedStorage.Callback {
  2. ContiguousPagedList(
  3. @NonNull ContiguousDataSource<K, V> dataSource,
  4. @NonNull Executor mainThreadExecutor,
  5. @NonNull Executor backgroundThreadExecutor,
  6. @Nullable BoundaryCallback<V> boundaryCallback,
  7. @NonNull Config config,
  8. final @Nullable K key,
  9. int lastLoad) {
  10. super(new PagedStorage<V>(), mainThreadExecutor, backgroundThreadExecutor,
  11. boundaryCallback, config);
  12. mDataSource = dataSource;
  13. mLastLoad = lastLoad;
  14. // 若当前datasource已经被置为无效了,则不会触发初始化数据加载的逻辑
  15. if (mDataSource.isInvalid()) {
  16. detach();
  17. } else {
  18. // 触发初始化数据的加载
  19. // 在DataSource的loadInitial方法中可以进行数据加载等处理
  20. // mDataSource.dispatchLoadInitial触发了已经配置的DataSource的loadInitial方法
  21. //
  22. // 从而触发了页面初始化数据的加载
  23. // 其中mReceiver参数表示接收网络数据加载成功之后的callback
  24. // PageResult.Receiver是内部类,它会判断本次分页回来的数据是初始化数据,
  25. // 还是分页数据,用以确定分页的状态
  26. // 而数据最终都是会被存储到PagedStorage<T>类中,它实际上是一个按页存储数据的ArrayList
  27. mDataSource.dispatchLoadInitial(key,
  28. mConfig.initialLoadSizeHint,
  29. mConfig.pageSize,
  30. mConfig.enablePlaceholders,
  31. mMainThreadExecutor,
  32. mReceiver);
  33. }
  34. mShouldTrim = mDataSource.supportsPageDropping()
  35. && mConfig.maxSize != Config.MAX_SIZE_UNBOUNDED;
  36. }
  37. }

【3.4】触发 Paging 的分页数据的加载

  1. // 触发分页数据的加载
  2. public abstract class PagedListAdapter<T, VH extends RecyclerView.ViewHolder>
  3. extends RecyclerView.Adapter<VH> {
  4. final AsyncPagedListDiffer<T> mDiffer;
  5. @Nullable
  6. protected T getItem(int position) {
  7. return mDiffer.getItem(position);
  8. }
  9. }
  10. public class AsyncPagedListDiffer<T> {
  11. @Nullable
  12. public T getItem(int index) {
  13. if (mPagedList == null) {
  14. if (mSnapshot == null) {
  15. throw new IndexOutOfBoundsException(
  16. "Item count is zero, getItem() call is invalid");
  17. } else {
  18. return mSnapshot.get(index);
  19. }
  20. }
  21. mPagedList.loadAround(index);
  22. return mPagedList.get(index);
  23. }
  24. }
  25. public abstract class PagedList<T> extends AbstractList<T> {
  26. public void loadAround(int index) {
  27. if (index < 0 || index >= size()) {
  28. throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size());
  29. }
  30. mLastLoad = index + getPositionOffset();
  31. //
  32. loadAroundInternal(index);
  33. mLowestIndexAccessed = Math.min(mLowestIndexAccessed, index);
  34. mHighestIndexAccessed = Math.max(mHighestIndexAccessed, index);
  35. /*
  36. * mLowestIndexAccessed / mHighestIndexAccessed have been updated, so check if we need to
  37. * dispatch boundary callbacks. Boundary callbacks are deferred until last items are loaded,
  38. * and accesses happen near the boundaries.
  39. *
  40. * Note: we post here, since RecyclerView may want to add items in response, and this
  41. * call occurs in PagedListAdapter bind.
  42. */
  43. tryDispatchBoundaryCallbacks(true);
  44. }
  45. // 该方法中会计算列表当前滑动状态下列表后面还需要追加几条Item,
  46. // 以及列表前面还需要向前追加几条Item,从而实现Paging向前向后分页加载数据的功能
  47. abstract void loadAroundInternal(int index);
  48. }
  49. class ContiguousPagedList<K, V> extends PagedList<V> implements PagedStorage.Callback {
  50. @MainThread
  51. @Override
  52. protected void loadAroundInternal(int index) {
  53. int prependItems = getPrependItemsRequested(mConfig.prefetchDistance, index,
  54. mStorage.getLeadingNullCount());
  55. int appendItems = getAppendItemsRequested(mConfig.prefetchDistance, index,
  56. mStorage.getLeadingNullCount() + mStorage.getStorageCount());
  57. mPrependItemsRequested = Math.max(prependItems, mPrependItemsRequested);
  58. if (mPrependItemsRequested > 0) {
  59. //计算之后,如果需要向前追加的Item数量大于0,schedulePrepend则会触发DataSource的LoadBefore方法
  60. //在DataSource的LoadBefore方法中可以进行数据加载等处理
  61. schedulePrepend();
  62. }
  63. mAppendItemsRequested = Math.max(appendItems, mAppendItemsRequested);
  64. if (mAppendItemsRequested > 0) {
  65. //计算之后,如果需要向后追加的Item数量大于0,scheduleAppend则会触发DataSource的LoadAfter方法
  66. //在DataSource的LoadAfter方法中可以进行数据加载等处理
  67. scheduleAppend();
  68. }
  69. }
  70. }

【3.5】数据的显示

在对应具体类型的 DataSource 类中的 loadInitial / loadBefore / loadAfter 完成数据的加载之后,回调 LoadInitialCallback / LoadCallback 回调接口的 onResult 方法,该接口中的相关方法在 LoadInitialCallbackImpl / LoadCallbackImpl 类中实现,在具体实现的 onResult 方法方法中调用 DataSource.LoadCallbackHelper 类中的 dispatchResultToReceiver 分发获取到的数据;

  1. public abstract class DataSource<Key, Value> {
  2. static class LoadCallbackHelper<T> {
  3. // 在对应具体类型的 DataSource 类的 dispatchLoadInitial / dispatchLoadAfter / dispatchLoadBefore 方法中传入
  4. // 用于构造具体类型的 DataSource 类的 LoadCallbackImpl 类实例
  5. // 从而传递给具体类型的 DataSource 类的 loadInitial / loadBefore / loadAfter 方法用于触发获取数据后的回调
  6. final PageResult.Receiver<T> mReceiver;
  7. void dispatchResultToReceiver(final @NonNull PageResult<T> result) {
  8. Executor executor;
  9. synchronized (mSignalLock) {
  10. if (mHasSignalled) {
  11. throw new IllegalStateException(
  12. "callback.onResult already called, cannot call again.");
  13. }
  14. mHasSignalled = true;
  15. executor = mPostExecutor;
  16. }
  17. if (executor != null) {
  18. executor.execute(new Runnable() {
  19. @Override
  20. public void run() {
  21. // 调用 onPageResult 回调
  22. mReceiver.onPageResult(mResultType, result);
  23. }
  24. });
  25. } else {
  26. // 调用 onPageResult 回调
  27. mReceiver.onPageResult(mResultType, result);
  28. }
  29. }
  30. }
  31. }
  32. public abstract class PagedList<T> extends AbstractList<T> {
  33. // 在构造函数中初始化
  34. // 具体子类中构造对应的 *Storage 类实例
  35. final PagedStorage<T> mStorage;
  36. // mCallbacks 是在 PagedListAdapter 的 submitList 方法中通过调用 addWeakCallback 方法添加的
  37. private final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
  38. PagedList(@NonNull PagedStorage<T> storage,
  39. @NonNull Executor mainThreadExecutor,
  40. @NonNull Executor backgroundThreadExecutor,
  41. @Nullable BoundaryCallback<T> boundaryCallback,
  42. @NonNull Config config) {
  43. mStorage = storage;
  44. mMainThreadExecutor = mainThreadExecutor;
  45. mBackgroundThreadExecutor = backgroundThreadExecutor;
  46. mBoundaryCallback = boundaryCallback;
  47. mConfig = config;
  48. mRequiredRemainder = mConfig.prefetchDistance * 2 + mConfig.pageSize;
  49. }
  50. // notifyInserted / notifyChanged / notifyRemoved 实现类似
  51. // 遍历注册的回调实例并调用其中的 onInserted / onChanged / onRemoved 方法通知改变数据的数量和位置信息
  52. void notifyInserted(int position, int count) {
  53. if (count != 0) {
  54. for (int i = mCallbacks.size() - 1; i >= 0; i--) {
  55. final Callback callback = mCallbacks.get(i).get();
  56. if (callback != null) {
  57. callback.onInserted(position, count);
  58. }
  59. }
  60. }
  61. }
  62. }
  63. class ContiguousPagedList<K, V> extends PagedList<V> implements PagedStorage.Callback {
  64. PageResult.Receiver<V> mReceiver = new PageResult.Receiver<V>() {
  65. // Creation thread for initial synchronous load, otherwise main thread
  66. // Safe to access main thread only state - no other thread has reference during construction
  67. @AnyThread
  68. @Override
  69. public void onPageResult(@PageResult.ResultType int resultType,
  70. @NonNull PageResult<V> pageResult) {
  71. if (pageResult.isInvalid()) {
  72. detach();
  73. return;
  74. }
  75. if (isDetached()) {
  76. // No op, have detached
  77. return;
  78. }
  79. List<V> page = pageResult.page;
  80. // loadInitial 对应初始化状态 PageResult.INIT
  81. // 对于回调类型为 PageResult.INIT 调用 mStorage.init 方法处理
  82. if (resultType == PageResult.INIT) {
  83. // 在 mStorage.init 方法中触发 onInitialized 回调
  84. // 在 onInitialized 回调将通知数据改变的位置和数量信息
  85. mStorage.init(pageResult.leadingNulls, page, pageResult.trailingNulls,
  86. pageResult.positionOffset, ContiguousPagedList.this);
  87. if (mLastLoad == LAST_LOAD_UNSPECIFIED) {
  88. // Because the ContiguousPagedList wasn't initialized with a last load position,
  89. // initialize it to the middle of the initial load
  90. mLastLoad =
  91. pageResult.leadingNulls + pageResult.positionOffset + page.size() / 2;
  92. }
  93. } else {
  94. // if we end up trimming, we trim from side that's furthest from most recent access
  95. boolean trimFromFront = mLastLoad > mStorage.getMiddleOfLoadedRange();
  96. // is the new page big enough to warrant pre-trimming (i.e. dropping) it?
  97. boolean skipNewPage = mShouldTrim
  98. && mStorage.shouldPreTrimNewPage(
  99. mConfig.maxSize, mRequiredRemainder, page.size());
  100. // loadAfter 对应初始化状态 PageResult.APPEND
  101. if (resultType == PageResult.APPEND) {
  102. if (skipNewPage && !trimFromFront) {
  103. // don't append this data, drop it
  104. mAppendItemsRequested = 0;
  105. mAppendWorkerState = READY_TO_FETCH;
  106. } else {
  107. // 追加一页数据回调 onPageAppended 方法
  108. mStorage.appendPage(page, ContiguousPagedList.this);
  109. }
  110. // loadBefore:对应初始化状态PageResult.PREPEND
  111. } else if (resultType == PageResult.PREPEND) {
  112. if (skipNewPage && trimFromFront) {
  113. // don't append this data, drop it
  114. mPrependItemsRequested = 0;
  115. mPrependWorkerState = READY_TO_FETCH;
  116. } else {
  117. // 回溯一页数据回调 onPagePrepended 方法
  118. mStorage.prependPage(page, ContiguousPagedList.this);
  119. }
  120. } else {
  121. throw new IllegalArgumentException("unexpected resultType " + resultType);
  122. }
  123. if (mShouldTrim) {
  124. if (trimFromFront) {
  125. if (mPrependWorkerState != FETCHING) {
  126. if (mStorage.trimFromFront(
  127. mReplacePagesWithNulls,
  128. mConfig.maxSize,
  129. mRequiredRemainder,
  130. ContiguousPagedList.this)) {
  131. // trimmed from front, ensure we can fetch in that dir
  132. mPrependWorkerState = READY_TO_FETCH;
  133. }
  134. }
  135. } else {
  136. if (mAppendWorkerState != FETCHING) {
  137. if (mStorage.trimFromEnd(
  138. mReplacePagesWithNulls,
  139. mConfig.maxSize,
  140. mRequiredRemainder,
  141. ContiguousPagedList.this)) {
  142. mAppendWorkerState = READY_TO_FETCH;
  143. }
  144. }
  145. }
  146. }
  147. }
  148. if (mBoundaryCallback != null) {
  149. boolean deferEmpty = mStorage.size() == 0;
  150. boolean deferBegin = !deferEmpty
  151. && resultType == PageResult.PREPEND
  152. && pageResult.page.size() == 0;
  153. boolean deferEnd = !deferEmpty
  154. && resultType == PageResult.APPEND
  155. && pageResult.page.size() == 0;
  156. deferBoundaryCallbacks(deferEmpty, deferBegin, deferEnd);
  157. }
  158. }
  159. };
  160. @MainThread
  161. @Override
  162. public void onInitialized(int count) {
  163. // 调用 PagedList 类的 notifyInserted 通知所有已注册回调实例数据改变的数量与位置信息
  164. notifyInserted(0, count);
  165. // simple heuristic to decide if, when dropping pages, we should replace with placeholders
  166. mReplacePagesWithNulls =
  167. mStorage.getLeadingNullCount() > 0 || mStorage.getTrailingNullCount() > 0;
  168. }
  169. @MainThread
  170. @Override
  171. public void onPageAppended(int endPosition, int changedCount, int addedCount) {
  172. // consider whether to post more work, now that a page is fully appended
  173. mAppendItemsRequested = mAppendItemsRequested - changedCount - addedCount;
  174. mAppendWorkerState = READY_TO_FETCH;
  175. if (mAppendItemsRequested > 0) {
  176. // not done appending, keep going
  177. // 持续加载数据直到一页数据加载完毕
  178. scheduleAppend();
  179. }
  180. // finally dispatch callbacks, after append may have already been scheduled
  181. // 调用 PagedList 类的 notifyInserted / notifyInserted 通知所有已注册回调实例数据改变的数量与位置信息
  182. notifyChanged(endPosition, changedCount);
  183. notifyInserted(endPosition + changedCount, addedCount);
  184. }
  185. @MainThread
  186. @Override
  187. public void onPagePrepended(int leadingNulls, int changedCount, int addedCount) {
  188. // consider whether to post more work, now that a page is fully prepended
  189. mPrependItemsRequested = mPrependItemsRequested - changedCount - addedCount;
  190. mPrependWorkerState = READY_TO_FETCH;
  191. if (mPrependItemsRequested > 0) {
  192. // not done prepending, keep going
  193. // 继续回溯直到一页数据回溯完毕
  194. schedulePrepend();
  195. }
  196. // finally dispatch callbacks, after prepend may have already been scheduled
  197. // 调用 PagedList 类的 notifyInserted / notifyInserted 通知所有已注册回调实例数据改变的数量与位置信息
  198. notifyChanged(leadingNulls, changedCount);
  199. notifyInserted(0, addedCount);
  200. // 调整偏移信息
  201. offsetAccessIndices(addedCount);
  202. }
  203. }

【3.6】ComputableLiveData 类关键代码分析

  1. public abstract class ComputableLiveData<T> {
  2. public ComputableLiveData(@NonNull Executor executor) {
  3. mExecutor = executor;
  4. // 在构造函数中创建了一个LiveData对象,并且复写了其onActive方法
  5. // 该方法当且仅当有第一个Observer被注册到LiveData的时候被调用
  6. // 而当onActive被调用的时候,便使用线程池执行RefreshRunnable,即触发了compute方法
  7. mLiveData = new LiveData<T>() {
  8. @Override
  9. protected void onActive() {
  10. mExecutor.execute(mRefreshRunnable);
  11. }
  12. };
  13. }
  14. public void invalidate() {
  15. // 在主线程执行 mInvalidationRunnable 中的方法
  16. ArchTaskExecutor.getInstance().executeOnMainThread(mInvalidationRunnable);
  17. }
  18. @VisibleForTesting
  19. final Runnable mInvalidationRunnable = new Runnable() {
  20. @MainThread
  21. @Override
  22. public void run() {
  23. boolean isActive = mLiveData.hasActiveObservers();
  24. if (mInvalid.compareAndSet(false, true)) {
  25. if (isActive) {
  26. // 使用线程池执行RefreshRunnable,即触发了compute方法
  27. mExecutor.execute(mRefreshRunnable);
  28. }
  29. }
  30. }
  31. };
  32. final Runnable mRefreshRunnable = new Runnable() {
  33. @WorkerThread
  34. @Override
  35. public void run() {
  36. boolean computed;
  37. do {
  38. computed = false;
  39. // compute can happen only in 1 thread but no reason to lock others.
  40. if (mComputing.compareAndSet(false, true)) {
  41. // as long as it is invalid, keep computing.
  42. try {
  43. T value = null;
  44. while (mInvalid.compareAndSet(true, false)) {
  45. computed = true;
  46. // 调用 compute() 方法
  47. value = compute();
  48. }
  49. if (computed) {
  50. // 发送数据更新通知
  51. mLiveData.postValue(value);
  52. }
  53. } finally {
  54. // release compute lock
  55. mComputing.set(false);
  56. }
  57. }
  58. // check invalid after releasing compute lock to avoid the following scenario.
  59. // Thread A runs compute()
  60. // Thread A checks invalid, it is false
  61. // Main thread sets invalid to true
  62. // Thread B runs, fails to acquire compute lock and skips
  63. // Thread A releases compute lock
  64. // We've left invalid in set state. The check below recovers.
  65. } while (computed && mInvalid.get());
  66. }
  67. };
  68. // 虚方法,在LivePagedListBuilder中有唯一实现
  69. protected abstract T compute();
  70. // 获取在构造函数中创建的LiveData对象
  71. @NonNull
  72. public LiveData<T> getLiveData() {
  73. return mLiveData;
  74. }
  75. }

参考
本博客为博主的学习实践总结,并参考了众多博主的博文,在此表示感谢,博主若有不足之处,请批评指正。

【1】跟架构师学Jetpack

【2】即学即用Android Jetpack - Paging

【3】Android Jetpack 之分页:paging

【4】Android Jetpack架构组件之 Paging(使用、源码篇)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/286474?site
推荐阅读
相关标签
  

闽ICP备14008679号