banner
jzman

jzman

Coding、思考、自觉。
github

Android Jetpack组件之Paging Library篇

阅读本文之前,可先阅读同系列 Android Jetpack 组件文章如下 :

本文将介绍 Paging Library 库的使用,其源码解析将在下篇文章中介绍,Paging Library 组件是 Android Jetpack 的一部分,是 Google 退出的官方分页组件,如果项目中使用了 Google 新推出的官方架构组件,如 LiveData、Lifecycle、ViewModel 等,就可以尝试将该分页组件引入自己的项目,它的优点是无痕加载更多数据,一定程度上提高用户体验。

简述一下使用 pageing 组件分页加载数据的过程,DataSource 负责从网络或数据库加载数据,将数据存储在 PagedList 中,使用 submitList 提交数据到 PagedListAdapter, 当数据发生变化时会在后台线程中计算数据差异,最后 PagedListAdapter 通知 RecyclerView 更新数据。

  1. 准备数据
  2. 引入 Paging Library 组件
  3. 自定义 DataSource
  4. 配置分页参数
  5. 加载显示数据
  6. 测试效果
  7. 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 监听加载的数据,然后使用 sumbitList 将数据提交给 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);
            }
        });
    }
}

测试效果#

image

以上就是 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
    boolean wasInactive = LiveData.this.mActiveCount == 0;
    // mActiveCount大于0
    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 的 Ruuable 中更新的,在 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 方法只在初始化的时候只回调过一次,但是却能够不断加载下一页数据的原因。

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。