赞
踩
【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 基本使用示例
- // 构建PagedList.Config对象,用以声明以何种方式分页
- PagedList.Config config = new PagedList.Config.Builder()
- .setPageSize(10) // 每次分页加载条目数目
- .setInitialLoadSizeHint(12) // 初始化数据加载的条目数目
- .build();
-
- // 创建数据源工厂类,用来创建数据提供者
- DataSource.Factory factory = new DataSource.Factory() {
- @NonNull
- @Override
- public DataSource create() {
- if (dataSource == null || dataSource.isInvalid()) {
- dataSource = createDataSource();
- }
- return dataSource;
- }
- };
-
- // BoundaryCallback
- // Signals when a PagedList has reached the end of available data
- // PagedList 的数据加载到达边界的回调方法
- PagedList.BoundaryCallback<T> callback = new PagedList.BoundaryCallback<T>() {
- @Override
- public void onZeroItemsLoaded() {
- // 新提交的PagedList中没有数据
- boundaryPageData.postValue(false);
- }
-
- @Override
- public void onItemAtFrontLoaded(@NonNull T itemAtFront) {
- // 新提交的PagedList中第一条数据被加载到列表上
- boundaryPageData.postValue(true);
- }
-
- @Override
- public void onItemAtEndLoaded(@NonNull T itemAtEnd) {
- // 新提交的PagedList中最后一条数据被加载到列表上
- }
- };
-
- // 构建一个能够触发加载页面初始化数据的LiveData对象
- LiveData<PagedList<T>> pageData = new LivePagedListBuilder(factory, config)
- .setInitialLoadKey(0)
- .setBoundaryCallback(callback)
- .build();
-
- // 用前面构建出来的LiveData对象注册一个Observer观察者,便可以触发页面初始化数据的加载了
- PagingViewModel viewModel = new ViewModelProvider(this).get(PagingViewModel.class);
- // 触发初始化数据加载
- viewModel.getPageData().observe(this, new Observer<PagedList<String>>() {
- @Override
- public void onChanged(PagedList<String> pagedList) {
- pagingAdapter.submitList(pagedList);
- }
- });
【3】Paging + LiveData 关键代码分析
【3.0】Paging 的工作流程
【3.1】创建 LiveData 对象
- public final class LivePagedListBuilder<Key, Value> {
-
- @NonNull
- @SuppressLint("RestrictedApi")
- public LiveData<PagedList<Value>> build() {
- // 构造LiveData<PagedList<Value>>类型的实例
- return create(mInitialLoadKey, mConfig, mBoundaryCallback, mDataSourceFactory,
- ArchTaskExecutor.getMainThreadExecutor(), mFetchExecutor);
- }
-
- @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) {
- // 该方法直接新建了一个ComputableLiveData并复写了其compute方法
- // 最终返回ComputableLiveData中的LiveData实例
- return new ComputableLiveData<PagedList<Value>>(fetchExecutor) {
- @Nullable
- private PagedList<Value> mList;
- @Nullable
- private DataSource<Key, Value> mDataSource;
-
- // 新建数据源无效事件回调实例并在回调方法中调用了ComputableLiveData的invalidate方法
- // 即触发了ComputableLiveData的compute方法
- // Invalidation callback for DataSource
- //
- // mCallback被添加到DataSource类的mOnInvalidatedCallbacks成员变量中
- // private CopyOnWriteArrayList<InvalidatedCallback>
- // mOnInvalidatedCallbacks = new CopyOnWriteArrayList<>();
- // 在DataSource类的invalidate方法中遍历mOnInvalidatedCallbacks中保存的回调实例并触发其回调方法
- // public void invalidate()
- 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() {
- // 初始化initializeKey,该值要么由用户指定,要么为PagedList中最新的值
- @Nullable Key initializeKey = initialLoadKey;
- if (mList != null) {
- initializeKey = (Key) mList.getLastKey();
- }
-
- do {
- // 删除原有的DataSource中的InvalidatedCallback回调实例
- if (mDataSource != null) {
- mDataSource.removeInvalidatedCallback(mCallback);
- }
- // 重新创建DataSource
- mDataSource = dataSourceFactory.create();
- // 添加InvalidatedCallback回调用以监听该DataSource被置为无效的事件
- // 一旦DataSource被置为无效,则不能在提供数据,
- // 但会再次触发该方法(compute)再次创建一个新的mDataSource
- mDataSource.addInvalidatedCallback(mCallback);
- // 新建PagedList实例
- mList = new PagedList.Builder<>(mDataSource, config)
- .setNotifyExecutor(notifyExecutor)
- .setFetchExecutor(fetchExecutor)
- .setBoundaryCallback(boundaryCallback)
- .setInitialKey(initializeKey)
- .build();
- } while (mList.isDetached());
- return mList;
- }
- }.getLiveData();
- }
-
- }
【3.2】List UI 显示刷新回调实例的注册
创建了 LiveData 对象并将其与宿主建立观察关系之后,会触发一次 Observer 的 onChanged 回调,在该回调中调用 public void submitList(@Nullable PagedList<T> pagedList);方法设置新的待显示的列表并在此过程中添加了针对该列表显示相关的回调实例;
- public abstract class PagedListAdapter<T, VH extends RecyclerView.ViewHolder>
- extends RecyclerView.Adapter<VH> {
-
- final AsyncPagedListDiffer<T> mDiffer;
-
- public void submitList(@Nullable PagedList<T> pagedList) {
- // 调用 AsyncPagedListDiffer 类实例的 submitList 方法
- // 目的可以实现列表数据差异增量更新
- mDiffer.submitList(pagedList);
- }
-
- }
-
- public class AsyncPagedListDiffer<T> {
-
- public AsyncPagedListDiffer(@NonNull RecyclerView.Adapter adapter,
- @NonNull DiffUtil.ItemCallback<T> diffCallback) {
- // 新建 mUpdateCallback 实例
- mUpdateCallback = new AdapterListUpdateCallback(adapter);
- mConfig = new AsyncDifferConfig.Builder<>(diffCallback).build();
- }
-
- // mPagedListCallback 的相关回调方法将回调委托给 mUpdateCallback 相关回调处理
- // 从而实现对 RecyclerView 的具体 Adapter 的通知
- private PagedList.Callback mPagedListCallback = new PagedList.Callback() {
- @Override
- public void onInserted(int position, int count) {
- mUpdateCallback.onInserted(position, count);
- }
-
- @Override
- public void onRemoved(int position, int count) {
- mUpdateCallback.onRemoved(position, count);
- }
-
- @Override
- public void onChanged(int position, int count) {
- // NOTE: pass a null payload to convey null -> item
- mUpdateCallback.onChanged(position, count, null);
- }
- };
-
- public void submitList(@Nullable final PagedList<T> pagedList,
- @Nullable final Runnable commitCallback) {
-
- // 添加 PagedList.Callback 实例 mPagedListCallback
- pagedList.addWeakCallback(null, mPagedListCallback);
- }
- }
-
- public final class AdapterListUpdateCallback implements ListUpdateCallback {
- @NonNull
- private final RecyclerView.Adapter mAdapter;
-
- /**
- * Creates an AdapterListUpdateCallback that will dispatch update events to the given adapter.
- *
- * @param adapter The Adapter to send updates to.
- */
- public AdapterListUpdateCallback(@NonNull RecyclerView.Adapter adapter) {
- mAdapter = adapter;
- }
-
- /** {@inheritDoc} */
- // 通知 RecyclerView 的 Adapter 有 Item 插入
- @Override
- public void onInserted(int position, int count) {
- mAdapter.notifyItemRangeInserted(position, count);
- }
-
- /** {@inheritDoc} */
- // 通知 RecyclerView 的 Adapter 有 Item 移除
- @Override
- public void onRemoved(int position, int count) {
- mAdapter.notifyItemRangeRemoved(position, count);
- }
-
- /** {@inheritDoc} */
- // 通知 RecyclerView 的 Adapter 有 Item 移动
- @Override
- public void onMoved(int fromPosition, int toPosition) {
- mAdapter.notifyItemMoved(fromPosition, toPosition);
- }
-
- /** {@inheritDoc} */
- // 通知 RecyclerView 的 Adapter 有 Item 改变
- @Override
- public void onChanged(int position, int count, Object payload) {
- mAdapter.notifyItemRangeChanged(position, count, payload);
- }
- }
【3.3】触发 Paging 的初始化数据的加载
- class ContiguousPagedList<K, V> extends PagedList<V> implements PagedStorage.Callback {
-
- 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 = dataSource;
- mLastLoad = lastLoad;
-
- // 若当前datasource已经被置为无效了,则不会触发初始化数据加载的逻辑
- if (mDataSource.isInvalid()) {
- detach();
- } else {
- // 触发初始化数据的加载
- // 在DataSource的loadInitial方法中可以进行数据加载等处理
- // mDataSource.dispatchLoadInitial触发了已经配置的DataSource的loadInitial方法
- //
- // 从而触发了页面初始化数据的加载
- // 其中mReceiver参数表示接收网络数据加载成功之后的callback
- // PageResult.Receiver是内部类,它会判断本次分页回来的数据是初始化数据,
- // 还是分页数据,用以确定分页的状态
- // 而数据最终都是会被存储到PagedStorage<T>类中,它实际上是一个按页存储数据的ArrayList
- mDataSource.dispatchLoadInitial(key,
- mConfig.initialLoadSizeHint,
- mConfig.pageSize,
- mConfig.enablePlaceholders,
- mMainThreadExecutor,
- mReceiver);
- }
- mShouldTrim = mDataSource.supportsPageDropping()
- && mConfig.maxSize != Config.MAX_SIZE_UNBOUNDED;
- }
-
- }
【3.4】触发 Paging 的分页数据的加载
- // 触发分页数据的加载
- public abstract class PagedListAdapter<T, VH extends RecyclerView.ViewHolder>
- extends RecyclerView.Adapter<VH> {
-
- final AsyncPagedListDiffer<T> mDiffer;
-
- @Nullable
- protected T getItem(int position) {
- return mDiffer.getItem(position);
- }
-
- }
-
-
- public class AsyncPagedListDiffer<T> {
-
- @Nullable
- public T getItem(int index) {
- if (mPagedList == null) {
- if (mSnapshot == null) {
- throw new IndexOutOfBoundsException(
- "Item count is zero, getItem() call is invalid");
- } else {
- return mSnapshot.get(index);
- }
- }
-
- mPagedList.loadAround(index);
- return mPagedList.get(index);
- }
-
- }
-
-
- public abstract class PagedList<T> extends AbstractList<T> {
-
- public void loadAround(int index) {
- if (index < 0 || index >= size()) {
- throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size());
- }
-
- mLastLoad = index + getPositionOffset();
- //
- loadAroundInternal(index);
-
- mLowestIndexAccessed = Math.min(mLowestIndexAccessed, index);
- mHighestIndexAccessed = Math.max(mHighestIndexAccessed, index);
-
- /*
- * mLowestIndexAccessed / mHighestIndexAccessed have been updated, so check if we need to
- * dispatch boundary callbacks. Boundary callbacks are deferred until last items are loaded,
- * and accesses happen near the boundaries.
- *
- * Note: we post here, since RecyclerView may want to add items in response, and this
- * call occurs in PagedListAdapter bind.
- */
- tryDispatchBoundaryCallbacks(true);
- }
-
- // 该方法中会计算列表当前滑动状态下列表后面还需要追加几条Item,
- // 以及列表前面还需要向前追加几条Item,从而实现Paging向前向后分页加载数据的功能
- abstract void loadAroundInternal(int index);
-
- }
-
- class ContiguousPagedList<K, V> extends PagedList<V> implements PagedStorage.Callback {
-
- @MainThread
- @Override
- protected void loadAroundInternal(int index) {
- int prependItems = getPrependItemsRequested(mConfig.prefetchDistance, index,
- mStorage.getLeadingNullCount());
- int appendItems = getAppendItemsRequested(mConfig.prefetchDistance, index,
- mStorage.getLeadingNullCount() + mStorage.getStorageCount());
-
- mPrependItemsRequested = Math.max(prependItems, mPrependItemsRequested);
- if (mPrependItemsRequested > 0) {
- //计算之后,如果需要向前追加的Item数量大于0,schedulePrepend则会触发DataSource的LoadBefore方法
- //在DataSource的LoadBefore方法中可以进行数据加载等处理
- schedulePrepend();
- }
-
- mAppendItemsRequested = Math.max(appendItems, mAppendItemsRequested);
- if (mAppendItemsRequested > 0) {
- //计算之后,如果需要向后追加的Item数量大于0,scheduleAppend则会触发DataSource的LoadAfter方法
- //在DataSource的LoadAfter方法中可以进行数据加载等处理
- scheduleAppend();
- }
- }
-
- }
【3.5】数据的显示
在对应具体类型的 DataSource 类中的 loadInitial / loadBefore / loadAfter 完成数据的加载之后,回调 LoadInitialCallback / LoadCallback 回调接口的 onResult 方法,该接口中的相关方法在 LoadInitialCallbackImpl / LoadCallbackImpl 类中实现,在具体实现的 onResult 方法方法中调用 DataSource.LoadCallbackHelper 类中的 dispatchResultToReceiver 分发获取到的数据;
- public abstract class DataSource<Key, Value> {
-
- static class LoadCallbackHelper<T> {
-
- // 在对应具体类型的 DataSource 类的 dispatchLoadInitial / dispatchLoadAfter / dispatchLoadBefore 方法中传入
- // 用于构造具体类型的 DataSource 类的 LoadCallbackImpl 类实例
- // 从而传递给具体类型的 DataSource 类的 loadInitial / loadBefore / loadAfter 方法用于触发获取数据后的回调
- final PageResult.Receiver<T> mReceiver;
-
- 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;
- }
-
- if (executor != null) {
- executor.execute(new Runnable() {
- @Override
- public void run() {
- // 调用 onPageResult 回调
- mReceiver.onPageResult(mResultType, result);
- }
- });
- } else {
- // 调用 onPageResult 回调
- mReceiver.onPageResult(mResultType, result);
- }
- }
-
- }
-
- }
-
- public abstract class PagedList<T> extends AbstractList<T> {
-
- // 在构造函数中初始化
- // 具体子类中构造对应的 *Storage 类实例
- final PagedStorage<T> mStorage;
-
- // mCallbacks 是在 PagedListAdapter 的 submitList 方法中通过调用 addWeakCallback 方法添加的
- private final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
- PagedList(@NonNull PagedStorage<T> storage,
- @NonNull Executor mainThreadExecutor,
- @NonNull Executor backgroundThreadExecutor,
- @Nullable BoundaryCallback<T> boundaryCallback,
- @NonNull Config config) {
- mStorage = storage;
- mMainThreadExecutor = mainThreadExecutor;
- mBackgroundThreadExecutor = backgroundThreadExecutor;
- mBoundaryCallback = boundaryCallback;
- mConfig = config;
- mRequiredRemainder = mConfig.prefetchDistance * 2 + mConfig.pageSize;
- }
-
- // notifyInserted / notifyChanged / notifyRemoved 实现类似
- // 遍历注册的回调实例并调用其中的 onInserted / onChanged / onRemoved 方法通知改变数据的数量和位置信息
- void notifyInserted(int position, int count) {
- if (count != 0) {
- for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- final Callback callback = mCallbacks.get(i).get();
- if (callback != null) {
- callback.onInserted(position, count);
- }
- }
- }
- }
-
- }
-
- class ContiguousPagedList<K, V> extends PagedList<V> implements PagedStorage.Callback {
-
- PageResult.Receiver<V> mReceiver = new PageResult.Receiver<V>() {
- // Creation thread for initial synchronous load, otherwise main thread
- // Safe to access main thread only state - no other thread has reference during construction
- @AnyThread
- @Override
- public void onPageResult(@PageResult.ResultType int resultType,
- @NonNull PageResult<V> pageResult) {
- if (pageResult.isInvalid()) {
- detach();
- return;
- }
-
- if (isDetached()) {
- // No op, have detached
- return;
- }
-
- List<V> page = pageResult.page;
- // loadInitial 对应初始化状态 PageResult.INIT
- // 对于回调类型为 PageResult.INIT 调用 mStorage.init 方法处理
- if (resultType == PageResult.INIT) {
- // 在 mStorage.init 方法中触发 onInitialized 回调
- // 在 onInitialized 回调将通知数据改变的位置和数量信息
- mStorage.init(pageResult.leadingNulls, page, pageResult.trailingNulls,
- pageResult.positionOffset, ContiguousPagedList.this);
- if (mLastLoad == LAST_LOAD_UNSPECIFIED) {
- // Because the ContiguousPagedList wasn't initialized with a last load position,
- // initialize it to the middle of the initial load
- mLastLoad =
- pageResult.leadingNulls + pageResult.positionOffset + page.size() / 2;
- }
- } else {
- // if we end up trimming, we trim from side that's furthest from most recent access
- boolean trimFromFront = mLastLoad > mStorage.getMiddleOfLoadedRange();
-
- // is the new page big enough to warrant pre-trimming (i.e. dropping) it?
- boolean skipNewPage = mShouldTrim
- && mStorage.shouldPreTrimNewPage(
- mConfig.maxSize, mRequiredRemainder, page.size());
- // loadAfter 对应初始化状态 PageResult.APPEND
- if (resultType == PageResult.APPEND) {
- if (skipNewPage && !trimFromFront) {
- // don't append this data, drop it
- mAppendItemsRequested = 0;
- mAppendWorkerState = READY_TO_FETCH;
- } else {
- // 追加一页数据回调 onPageAppended 方法
- mStorage.appendPage(page, ContiguousPagedList.this);
- }
- // loadBefore:对应初始化状态PageResult.PREPEND
- } else if (resultType == PageResult.PREPEND) {
- if (skipNewPage && trimFromFront) {
- // don't append this data, drop it
- mPrependItemsRequested = 0;
- mPrependWorkerState = READY_TO_FETCH;
- } else {
- // 回溯一页数据回调 onPagePrepended 方法
- mStorage.prependPage(page, ContiguousPagedList.this);
- }
- } else {
- throw new IllegalArgumentException("unexpected resultType " + resultType);
- }
-
- if (mShouldTrim) {
- if (trimFromFront) {
- if (mPrependWorkerState != FETCHING) {
- if (mStorage.trimFromFront(
- mReplacePagesWithNulls,
- mConfig.maxSize,
- mRequiredRemainder,
- ContiguousPagedList.this)) {
- // trimmed from front, ensure we can fetch in that dir
- mPrependWorkerState = READY_TO_FETCH;
- }
- }
- } else {
- if (mAppendWorkerState != FETCHING) {
- if (mStorage.trimFromEnd(
- mReplacePagesWithNulls,
- mConfig.maxSize,
- mRequiredRemainder,
- ContiguousPagedList.this)) {
- mAppendWorkerState = READY_TO_FETCH;
- }
- }
- }
- }
- }
-
- if (mBoundaryCallback != null) {
- boolean deferEmpty = mStorage.size() == 0;
- boolean deferBegin = !deferEmpty
- && resultType == PageResult.PREPEND
- && pageResult.page.size() == 0;
- boolean deferEnd = !deferEmpty
- && resultType == PageResult.APPEND
- && pageResult.page.size() == 0;
- deferBoundaryCallbacks(deferEmpty, deferBegin, deferEnd);
- }
- }
- };
-
- @MainThread
- @Override
- public void onInitialized(int count) {
- // 调用 PagedList 类的 notifyInserted 通知所有已注册回调实例数据改变的数量与位置信息
- notifyInserted(0, count);
- // simple heuristic to decide if, when dropping pages, we should replace with placeholders
- mReplacePagesWithNulls =
- mStorage.getLeadingNullCount() > 0 || mStorage.getTrailingNullCount() > 0;
- }
-
- @MainThread
- @Override
- public void onPageAppended(int endPosition, int changedCount, int addedCount) {
- // consider whether to post more work, now that a page is fully appended
- mAppendItemsRequested = mAppendItemsRequested - changedCount - addedCount;
- mAppendWorkerState = READY_TO_FETCH;
- if (mAppendItemsRequested > 0) {
- // not done appending, keep going
- // 持续加载数据直到一页数据加载完毕
- scheduleAppend();
- }
-
- // finally dispatch callbacks, after append may have already been scheduled
- // 调用 PagedList 类的 notifyInserted / notifyInserted 通知所有已注册回调实例数据改变的数量与位置信息
- notifyChanged(endPosition, changedCount);
- notifyInserted(endPosition + changedCount, addedCount);
- }
-
- @MainThread
- @Override
- public void onPagePrepended(int leadingNulls, int changedCount, int addedCount) {
- // consider whether to post more work, now that a page is fully prepended
- mPrependItemsRequested = mPrependItemsRequested - changedCount - addedCount;
- mPrependWorkerState = READY_TO_FETCH;
- if (mPrependItemsRequested > 0) {
- // not done prepending, keep going
- // 继续回溯直到一页数据回溯完毕
- schedulePrepend();
- }
-
- // finally dispatch callbacks, after prepend may have already been scheduled
- // 调用 PagedList 类的 notifyInserted / notifyInserted 通知所有已注册回调实例数据改变的数量与位置信息
- notifyChanged(leadingNulls, changedCount);
- notifyInserted(0, addedCount);
- // 调整偏移信息
- offsetAccessIndices(addedCount);
- }
-
- }
【3.6】ComputableLiveData 类关键代码分析
- public abstract class ComputableLiveData<T> {
-
- public ComputableLiveData(@NonNull Executor executor) {
- mExecutor = executor;
- // 在构造函数中创建了一个LiveData对象,并且复写了其onActive方法
- // 该方法当且仅当有第一个Observer被注册到LiveData的时候被调用
- // 而当onActive被调用的时候,便使用线程池执行RefreshRunnable,即触发了compute方法
- mLiveData = new LiveData<T>() {
- @Override
- protected void onActive() {
- mExecutor.execute(mRefreshRunnable);
- }
- };
- }
-
- public void invalidate() {
- // 在主线程执行 mInvalidationRunnable 中的方法
- ArchTaskExecutor.getInstance().executeOnMainThread(mInvalidationRunnable);
- }
-
- @VisibleForTesting
- final Runnable mInvalidationRunnable = new Runnable() {
- @MainThread
- @Override
- public void run() {
- boolean isActive = mLiveData.hasActiveObservers();
- if (mInvalid.compareAndSet(false, true)) {
- if (isActive) {
- // 使用线程池执行RefreshRunnable,即触发了compute方法
- mExecutor.execute(mRefreshRunnable);
- }
- }
- }
- };
-
- final Runnable mRefreshRunnable = new Runnable() {
- @WorkerThread
- @Override
- public void run() {
- boolean computed;
- do {
- computed = false;
- // compute can happen only in 1 thread but no reason to lock others.
- if (mComputing.compareAndSet(false, true)) {
- // as long as it is invalid, keep computing.
- try {
- T value = null;
- while (mInvalid.compareAndSet(true, false)) {
- computed = true;
- // 调用 compute() 方法
- value = compute();
- }
- if (computed) {
- // 发送数据更新通知
- mLiveData.postValue(value);
- }
- } finally {
- // release compute lock
- mComputing.set(false);
- }
- }
- // check invalid after releasing compute lock to avoid the following scenario.
- // Thread A runs compute()
- // Thread A checks invalid, it is false
- // Main thread sets invalid to true
- // Thread B runs, fails to acquire compute lock and skips
- // Thread A releases compute lock
- // We've left invalid in set state. The check below recovers.
- } while (computed && mInvalid.get());
- }
- };
- // 虚方法,在LivePagedListBuilder中有唯一实现
- protected abstract T compute();
- // 获取在构造函数中创建的LiveData对象
- @NonNull
- public LiveData<T> getLiveData() {
- return mLiveData;
- }
- }
参考
本博客为博主的学习实践总结,并参考了众多博主的博文,在此表示感谢,博主若有不足之处,请批评指正。
【1】跟架构师学Jetpack
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。