当前位置:   article > 正文

Android JetPack学习笔记-Paging_paging pagedstorage

paging pagedstorage

目录

1.Paging2.x

   (1)使用

    (2)源码分析

        【1】初次如何加载数据

        【2】初次刷新UI

        【3】后续的追加页面


1.Paging2.x

   (1)使用

        javabean:

  1. public class Person {
  2. private String id;
  3. private String name;
  4. private String sex;
  5. public String getId() {
  6. return id;
  7. }
  8. public void setId(String id) {
  9. this.id = id;
  10. }
  11. public String getName() {
  12. return name;
  13. }
  14. public void setName(String name) {
  15. this.name = name;
  16. }
  17. public String getSex() {
  18. return sex;
  19. }
  20. public void setSex(String sex) {
  21. this.sex = sex;
  22. }
  23. @Override
  24. public boolean equals(Object o) {
  25. if (this == o) return true;
  26. if (!(o instanceof Person)) return false;
  27. Person person = (Person) o;
  28. return getId().equals(person.getId()) &&
  29. getName().equals(person.getName()) &&
  30. getSex().equals(person.getSex());
  31. }
  32. @RequiresApi(api = Build.VERSION_CODES.KITKAT)
  33. @Override
  34. public int hashCode() {
  35. return Objects.hash(getId(), getName(), getSex());
  36. }
  37. @Override
  38. public String toString() {
  39. return "Person{" +
  40. "id='" + id + '\'' +
  41. ", name='" + name + '\'' +
  42. ", sex='" + sex + '\'' +
  43. '}';
  44. }
  45. }

        ViewModel

  1. public class PersonViewModel extends ViewModel {
  2. private final LiveData<PagedList<Person>> pagedListLiveData;
  3. public PersonViewModel() {
  4. // @1 Factory 的创建
  5. DataSource.Factory<Integer, Person> factory = new PersonDataSourceFactory();
  6. /*PagedList.Config pConfig = new PagedList.Config.Builder()
  7. .setPageSize(20)
  8. .setEnablePlaceholders(true)
  9. .setInitialLoadSizeHint(20)
  10. .build();*/
  11. // @1 Factory
  12. pagedListLiveData = new LivePagedListBuilder<Integer, Person>(factory,
  13. new PagedList.Config.Builder()
  14. .setEnablePlaceholders(true)
  15. .setPageSize(20)
  16. .build()).build();
  17. }
  18. public LiveData<PagedList<Person>> getPagedListLiveData() {
  19. return pagedListLiveData;
  20. }
  21. }

        Adapter

  1. public class RecyclerPagingAdapter extends PagedListAdapter<Person, RecyclerPagingAdapter.MyRecyclerViewHolder> {
  2. //需要oldPerson与新 newPerson 比较才能得出变化的数据
  3. private static DiffUtil.ItemCallback<Person> DIFF_STUDENT =
  4. new DiffUtil.ItemCallback<Person>() {
  5. // 判断Item是否已经存在
  6. @Override
  7. public boolean areItemsTheSame(Person oldPerson, Person newPerson) {
  8. return oldPerson.getId().equals(newPerson.getId());
  9. }
  10. // 如果Item已经存在则会调用此方法,判断Item的内容是否一致
  11. @Override
  12. public boolean areContentsTheSame(Person oldPerson, Person newPerson) {
  13. return oldPerson.equals(newPerson);
  14. }
  15. };
  16. protected RecyclerPagingAdapter() {
  17. super(DIFF_STUDENT);
  18. }
  19. ......
  20. }

        Factory

  1. public class PersonDataSourceFactory extends DataSource.Factory<Integer, Person> {
  2. @NonNull
  3. @Override
  4. public DataSource<Integer, Person> create() {
  5. return new CustomPageDataSource(new DataRepository());
  6. }
  7. }

        

  1. public class CustomPageDataSource extends PageKeyedDataSource<Integer, Person> {
  2. private DataRepository dataRepository;
  3. CustomPageDataSource(DataRepository dataRepository) {
  4. this.dataRepository = dataRepository;
  5. }
  6. // loadInitial 初始加载数据
  7. @Override
  8. public void loadInitial(@NonNull LoadInitialParams<Integer> params, @NonNull LoadInitialCallback<Integer, Person> callback) {
  9. List<Person> dataList = dataRepository.initData(params.requestedLoadSize);
  10. callback.onResult(dataList, null, 2);
  11. }
  12. // loadBefore 向前分页加载数据
  13. @Override
  14. public void loadBefore(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, Person> callback) {
  15. List<Person> dataList = dataRepository.loadPageData(params.key, params.requestedLoadSize);
  16. if (dataList != null) {
  17. callback.onResult(dataList, params.key - 1);
  18. }
  19. }
  20. // loadAfter 向后分页加载数据
  21. @Override
  22. public void loadAfter(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, Person> callback) {
  23. List<Person> dataList = dataRepository.loadPageData(params.key, params.requestedLoadSize);
  24. if (dataList != null) {
  25. callback.onResult(dataList, params.key + 1);
  26. }
  27. }
  28. }

        仓库类

  1. public class DataRepository {
  2. private List<Person> dataList = new ArrayList<>();
  3. public DataRepository() {
  4. for (int i = 0; i < 1000; i++) {
  5. Person person = new Person();
  6. person.setId("ID号是:" + i);
  7. person.setName("我名称:" + i);
  8. person.setSex("我性别:" + i);
  9. dataList.add(person);
  10. }
  11. }
  12. public List<Person> initData(int size) {
  13. return dataList.subList(0, size);
  14. }
  15. public List<Person> loadPageData(int page, int size) {
  16. int totalPage;
  17. if (dataList.size() % size == 0) {
  18. totalPage = dataList.size() / size;
  19. } else {
  20. totalPage = dataList.size() / size + 1;
  21. }
  22. if (page > totalPage || page < 1) {
  23. return null;
  24. }
  25. if (page == totalPage) {
  26. return dataList.subList((page - 1) * size, dataList.size());
  27. }
  28. return dataList.subList((page - 1) * size, page * size);
  29. }
  30. }

        activity

  1. protected void onCreate(Bundle savedInstanceState) {
  2. super.onCreate(savedInstanceState);
  3. setContentView(R.layout.activity_main);
  4. recyclerView = findViewById(R.id.recycle_view);
  5. recyclerPagingAdapter = new RecyclerPagingAdapter();
  6. personViewModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(PersonViewModel.class);
  7. personViewModel.getPagedListLiveData().observe(this, new Observer<PagedList<Person>>() {
  8. @Override
  9. public void onChanged(PagedList<Person> persons) {
  10. recyclerPagingAdapter.submitList(persons);
  11. }
  12. });
  13. recyclerView.setAdapter(recyclerPagingAdapter);
  14. recyclerView.setLayoutManager(new LinearLayoutManager(this));
  15. }

        这样我们就做到了自动分页,自动请求网络请求刷新UI。

    (2)源码分析

        【1】初次如何加载数据

        首先咱们从入口出发,我们在activity获取了viewmodel的livedata进行订阅。咱们就从viewmodel出发。看到了在viewmodel中通过LivePagedListBuilder建造者模式创建出来的livedata。

  1. public LiveData<PagedList<Value>> build() {
  2. return create(mInitialLoadKey, mConfig, mBoundaryCallback, mDataSourceFactory,
  3. ArchTaskExecutor.getMainThreadExecutor(), mFetchExecutor);
  4. }
  5. @SuppressLint("RestrictedApi")
  6. private static <Key, Value> LiveData<PagedList<Value>> create(
  7. @Nullable final Key initialLoadKey,
  8. @NonNull final PagedList.Config config,
  9. @Nullable final PagedList.BoundaryCallback boundaryCallback,
  10. @NonNull final DataSource.Factory<Key, Value> dataSourceFactory,
  11. @NonNull final Executor notifyExecutor,
  12. @NonNull final Executor fetchExecutor) {
  13. return new ComputableLiveData<PagedList<Value>>(fetchExecutor) {
  14. @Nullable
  15. private PagedList<Value> mList;
  16. @Nullable
  17. private DataSource<Key, Value> mDataSource;
  18. private final DataSource.InvalidatedCallback mCallback =
  19. new DataSource.InvalidatedCallback() {
  20. @Override
  21. public void onInvalidated() {
  22. invalidate();
  23. }
  24. };
  25. ......
  26. }.getLiveData();
  27. }

        他的建造者模式通过源码我们看到她给我们创建了一个ComputableLiveData的类。那具体实现我们就要去其内部看看。

  1. public ComputableLiveData(@NonNull Executor executor) {
  2. mExecutor = executor;//默认咱们传过来的是IO
  3. //创建一个livedata,当它所订阅的生命组件到达活跃状态时会触发
  4. mLiveData = new LiveData<T>() {
  5. @Override
  6. protected void onActive() {
  7. mExecutor.execute(mRefreshRunnable);
  8. }
  9. };
  10. }
  11. //返回构造方法里初始化的livedata
  12. public LiveData<T> getLiveData() {
  13. return mLiveData;
  14. }
  15. final Runnable mRefreshRunnable = new Runnable() {
  16. @WorkerThread
  17. @Override
  18. public void run() {
  19. boolean computed;
  20. do {
  21. computed = false;
  22. //通过CAS自旋来判断值
  23. //当初次时会满足条件
  24. if (mComputing.compareAndSet(false, true)) {
  25. try {
  26. T value = null;
  27. //当初次时会满足条件
  28. while (mInvalid.compareAndSet(true, false)) {
  29. computed = true;
  30. //计算值
  31. value = compute();
  32. }
  33. if (computed) {
  34. //由于咱们传过来的是IO线程,所以用postValue进行传值
  35. //这边livedata一传值,就会触发我们activity的订阅就会向adapter执行submitList,进行数据更新
  36. mLiveData.postValue(value);
  37. }
  38. } finally {
  39. mComputing.set(false);
  40. }
  41. }
  42. } while (computed && mInvalid.get());
  43. }
  44. };

        那首次数据进行改变咱们看到这边就大概明白了,但是它compute都做了些啥,咱们不知道。compute方法在我们之前返回的ComputableLiveData类中有实现。

  1. @override
  2. protected PagedList<Value> compute() {
  3. @Nullable Key initializeKey = initialLoadKey;
  4. if (mList != null) {
  5. initializeKey = (Key) mList.getLastKey();
  6. }
  7. do {
  8. if (mDataSource != null) {
  9. mDataSource.removeInvalidatedCallback(mCallback);
  10. }
  11. //获取咱们factory中create创建的数据源
  12. mDataSource = dataSourceFactory.create();
  13. mDataSource.addInvalidatedCallback(mCallback);
  14. //我们传过来的参数,几乎都在此被这个类构建使用
  15. mList = new PagedList.Builder<>(mDataSource, config)
  16. .setNotifyExecutor(notifyExecutor)//Main线程
  17. .setFetchExecutor(fetchExecutor)//IO线程
  18. .setBoundaryCallback(boundaryCallback)
  19. .setInitialKey(initializeKey)
  20. .build();
  21. } while (mList.isDetached());
  22. return mList;
  23. }

        看到参数几乎都用于构建PagedList,看看内部

  1. public PagedList<Value> build() {
  2. if (mNotifyExecutor == null) {
  3. throw new IllegalArgumentException("MainThreadExecutor required");
  4. }
  5. if (mFetchExecutor == null) {
  6. throw new IllegalArgumentException("BackgroundThreadExecutor required");
  7. }
  8. return PagedList.create(
  9. mDataSource,
  10. mNotifyExecutor,
  11. mFetchExecutor,
  12. mBoundaryCallback,
  13. mConfig,
  14. mInitialKey);
  15. }
  16. static <K, T> PagedList<T> create(@NonNull DataSource<K, T> dataSource,
  17. @NonNull Executor notifyExecutor,
  18. @NonNull Executor fetchExecutor,
  19. @Nullable BoundaryCallback<T> boundaryCallback,
  20. @NonNull Config config,
  21. @Nullable K key) {
  22. //判断源是否连续 || 是否存在占位
  23. if (dataSource.isContiguous() || !config.enablePlaceholders) {
  24. int lastLoad = ContiguousPagedList.LAST_LOAD_UNSPECIFIED;
  25. //不存在占位 且 源不连续
  26. if (!dataSource.isContiguous()) {
  27. //创建单源PositionalDataSource
  28. dataSource = (DataSource<K, T>) ((PositionalDataSource<T>) dataSource)
  29. .wrapAsContiguousWithoutPlaceholders();
  30. if (key != null) {
  31. lastLoad = (Integer) key;
  32. }
  33. }
  34. ContiguousDataSource<K, T> contigDataSource = (ContiguousDataSource<K, T>) dataSource;
  35. //创建ContiguousPagedList实例
  36. return new ContiguousPagedList<>(contigDataSource,
  37. notifyExecutor,
  38. fetchExecutor,
  39. boundaryCallback,
  40. config,
  41. key,
  42. lastLoad);
  43. } else {
  44. return new TiledPagedList<>((PositionalDataSource<T>) dataSource,
  45. notifyExecutor,
  46. fetchExecutor,
  47. boundaryCallback,
  48. config,
  49. (key != null) ? (Integer) key : 0);
  50. }
  51. }

        我们以连续源为例,ContiguousPagedList继续看。

  1. ContiguousPagedList(
  2. @NonNull ContiguousDataSource<K, V> dataSource,
  3. @NonNull Executor mainThreadExecutor,
  4. @NonNull Executor backgroundThreadExecutor,
  5. @Nullable BoundaryCallback<V> boundaryCallback,
  6. @NonNull Config config,
  7. final @Nullable K key,
  8. int lastLoad) {
  9. super(new PagedStorage<V>(), mainThreadExecutor, backgroundThreadExecutor,
  10. boundaryCallback, config);
  11. mDataSource = dataSource;
  12. mLastLoad = lastLoad;
  13. //判断数据源是否失效
  14. if (mDataSource.isInvalid()) {
  15. detach();
  16. } else {
  17. //初始化加载
  18. mDataSource.dispatchLoadInitial(key,
  19. mConfig.initialLoadSizeHint,
  20. mConfig.pageSize,
  21. mConfig.enablePlaceholders,
  22. mMainThreadExecutor,
  23. mReceiver);
  24. }
  25. mShouldTrim = mDataSource.supportsPageDropping()
  26. && mConfig.maxSize != Config.MAX_SIZE_UNBOUNDED;
  27. }

        到这咱们就到了初始化加载数据了,看它的内部是个抽象类。我们的例子以PageKeyedDataSource为例。

  1. @Override
  2. final void dispatchLoadInitial(@Nullable Key key, int initialLoadSize, int pageSize,
  3. boolean enablePlaceholders, @NonNull Executor mainThreadExecutor,
  4. @NonNull PageResult.Receiver<Value> receiver) {
  5. LoadInitialCallbackImpl<Key, Value> callback =
  6. new LoadInitialCallbackImpl<>(this, enablePlaceholders, receiver);//(1)创建了一个回调
  7. loadInitial(new LoadInitialParams<Key>(initialLoadSize, enablePlaceholders), callback);//(2)真正加载数据的方法
  8. callback.mCallbackHelper.setPostExecutor(mainThreadExecutor);//设置回调的线程为主线程
  9. }

    (1)

  1. LoadInitialCallbackImpl(@NonNull PageKeyedDataSource<Key, Value> dataSource,
  2. boolean countingEnabled, @NonNull PageResult.Receiver<Value> receiver) {
  3. mCallbackHelper = new LoadCallbackHelper<>(
  4. dataSource, PageResult.INIT, null, receiver);//设置状态为INIT
  5. mDataSource = dataSource;
  6. mCountingEnabled = countingEnabled;
  7. }

    (2)loadInitial是我们实现的DataSource中实现的方法

  1. @Override
  2. public void loadInitial(@NonNull LoadInitialParams<Integer> params, @NonNull LoadInitialCallback<Integer, Person> callback) {
  3. //在这里我们就进行网络请求数据了
  4. List<Person> dataList = dataRepository.initData(params.requestedLoadSize);
  5. //调用回调的onResult
  6. callback.onResult(dataList, null, 2);
  7. }

         到这,咱们就跑到了LoadInitialCallback.onResult方法里了。

  1. public void onResult(@NonNull List<Value> data, @Nullable Key previousPageKey,
  2. @Nullable Key nextPageKey) {
  3. if (!mCallbackHelper.dispatchInvalidResultIfInvalid()) {
  4. //key值赋值
  5. mDataSource.initKeys(previousPageKey, nextPageKey);
  6. //主要分发结果
  7. mCallbackHelper.dispatchResultToReceiver(new PageResult<>(data, 0, 0, 0));
  8. }
  9. }
  1. //判断是否有效
  2. boolean dispatchInvalidResultIfInvalid() {
  3. if (mDataSource.isInvalid()) {
  4. dispatchResultToReceiver(PageResult.<T>getInvalidResult());
  5. return true;
  6. }
  7. return false;
  8. }
  9. void dispatchResultToReceiver(final @NonNull PageResult<T> result) {
  10. Executor executor;
  11. synchronized (mSignalLock) {
  12. if (mHasSignalled) {
  13. throw new IllegalStateException(
  14. "callback.onResult already called, cannot call again.");
  15. }
  16. mHasSignalled = true;
  17. executor = mPostExecutor;//主线程
  18. }
  19. if (executor != null) {
  20. executor.execute(new Runnable() {
  21. @Override
  22. public void run() {
  23. //计算页面结果,mReceiver在ContiguousPagedList类中为成员变量
  24. mReceiver.onPageResult(mResultType, result);
  25. }
  26. });
  27. } else {
  28. mReceiver.onPageResult(mResultType, result);
  29. }
  30. }

        最终执行了onPageResult

  1. public void onPageResult(@PageResult.ResultType int resultType,
  2. @NonNull PageResult<V> pageResult) {
  3. ......
  4. List<V> page = pageResult.page;
  5. if (resultType == PageResult.INIT) {
  6. //状态为初始化时执行
  7. mStorage.init(pageResult.leadingNulls, page, pageResult.trailingNulls,
  8. pageResult.positionOffset, ContiguousPagedList.this);
  9. if (mLastLoad == LAST_LOAD_UNSPECIFIED) {
  10. mLastLoad =
  11. pageResult.leadingNulls + pageResult.positionOffset + page.size() / 2;
  12. }
  13. } else {
  14. boolean trimFromFront = mLastLoad > mStorage.getMiddleOfLoadedRange();
  15. boolean skipNewPage = mShouldTrim
  16. && mStorage.shouldPreTrimNewPage(
  17. mConfig.maxSize, mRequiredRemainder, page.size());
  18. if (resultType == PageResult.APPEND) {
  19. //状态为追加页面
  20. if (skipNewPage && !trimFromFront) {
  21. //没有增加任何数据
  22. mAppendItemsRequested = 0;
  23. mAppendWorkerState = READY_TO_FETCH;
  24. } else {
  25. //主要执行
  26. mStorage.appendPage(page, ContiguousPagedList.this);
  27. }
  28. } else if (resultType == PageResult.PREPEND) {
  29. if (skipNewPage && trimFromFront) {
  30. //没有增加任何数据
  31. mPrependItemsRequested = 0;
  32. mPrependWorkerState = READY_TO_FETCH;
  33. } else {
  34. //主要执行
  35. mStorage.prependPage(page, ContiguousPagedList.this);
  36. }
  37. } else {
  38. throw new IllegalArgumentException("unexpected resultType " + resultType);
  39. }
  40. ......
  41. }

        可以看到主要处理就在这边的onPageResult方法,调用了Storage进行我们的初始化、往后追加页面和向前追加页面。我们先看init。初始化整个流程还没走完。

  1. //PagedStorage中的方法
  2. void init(int leadingNulls, @NonNull List<T> page, int trailingNulls, int positionOffset,
  3. @NonNull Callback callback) {
  4. init(leadingNulls, page, trailingNulls, positionOffset);
  5. callback.onInitialized(size());
  6. }
  7. //ContiguousPagedList中的方法
  8. public void onInitialized(int count) {
  9. notifyInserted(0, count);
  10. // simple heuristic to decide if, when dropping pages, we should replace with placeholders
  11. mReplacePagesWithNulls =
  12. mStorage.getLeadingNullCount() > 0 || mStorage.getTrailingNullCount() > 0;
  13. }
  14. //PagedList中的方法
  15. void notifyInserted(int position, int count) {
  16. //由于我们初始化,所以当前mCallback中还没有存在任何的回调,所以无法通过回调去执行更新插入
  17. if (count != 0) {
  18. for (int i = mCallbacks.size() - 1; i >= 0; i--) {
  19. final Callback callback = mCallbacks.get(i).get();
  20. if (callback != null) {
  21. callback.onInserted(position, count);
  22. }
  23. }
  24. }
  25. }

        到这我们compute一路走下来就完全走完了,一路上我们通过factory创建了对应的dataSource,通过dataSource请求数据之后,进行了storage的初始化。最后就执行了livedata.postValue进行初次的传值。(当然如果创建了BoundaryCallback也会对应执行)。数据进行了改变,熟悉livedata就直到会走到我们activity中的observer方法里了。

        【2】初次刷新UI

  1. personViewModel.getPagedListLiveData().observe(this, new Observer<PagedList<Person>>() {
  2. @Override
  3. public void onChanged(PagedList<Person> persons) {
  4. recyclerPagingAdapter.submitList(persons);//提交数据入口
  5. }
  6. });
  1. //我们在adapter中创建的callback
  2. protected PagedListAdapter(@NonNull DiffUtil.ItemCallback<T> diffCallback) {
  3. mDiffer = new AsyncPagedListDiffer<>(this, diffCallback);
  4. mDiffer.addPagedListListener(mListener);
  5. }
  6. public void submitList(@Nullable PagedList<T> pagedList) {
  7. mDiffer.submitList(pagedList);
  8. }
  1. public AsyncPagedListDiffer(@NonNull RecyclerView.Adapter adapter,
  2. @NonNull DiffUtil.ItemCallback<T> diffCallback) {
  3. mUpdateCallback = new AdapterListUpdateCallback(adapter);
  4. mConfig = new AsyncDifferConfig.Builder<>(diffCallback).build();
  5. }
  6. public void submitList(@Nullable final PagedList<T> pagedList) {
  7. submitList(pagedList, null);
  8. }
  9. public void submitList(@Nullable final PagedList<T> pagedList,
  10. @Nullable final Runnable commitCallback) {
  11. ......
  12. final PagedList<T> previous = (mSnapshot != null) ? mSnapshot : mPagedList;
  13. if (pagedList == null) {
  14. int removedCount = getItemCount();
  15. if (mPagedList != null) {
  16. mPagedList.removeWeakCallback(mPagedListCallback);
  17. mPagedList = null;
  18. } else if (mSnapshot != null) {
  19. mSnapshot = null;
  20. }
  21. mUpdateCallback.onRemoved(0, removedCount);
  22. onCurrentListChanged(previous, null, commitCallback);
  23. return;
  24. }
  25. if (mPagedList == null && mSnapshot == null) {
  26. //上一页数据为空 且 快照不存在
  27. mPagedList = pagedList;
  28. //之前说初始化所以mCallback为空,到这里向我们mCallback存回调
  29. pagedList.addWeakCallback(null, mPagedListCallback);
  30. //真正的刷新
  31. mUpdateCallback.onInserted(0, pagedList.size());
  32. onCurrentListChanged(null, pagedList, commitCallback);
  33. return;
  34. }
  35. ......
  36. }
  1. private final RecyclerView.Adapter mAdapter;
  2. public AdapterListUpdateCallback(@NonNull RecyclerView.Adapter adapter) {
  3. mAdapter = adapter;
  4. }
  5. @Override
  6. public void onInserted(int position, int count) {
  7. mAdapter.notifyItemRangeInserted(position, count);
  8. }

        到此为止,咱们的刷新UI就结束了,但是我们仔细看它是如何对adapter进行修改的。我们类实现自PagedListAdapter,看看它怎么存我们数据的。

  1. @Nullable
  2. protected T getItem(int position) {
  3. return mDiffer.getItem(position);
  4. }
  5. @Override
  6. public int getItemCount() {
  7. return mDiffer.getItemCount();
  8. }
  1. public int getItemCount() {
  2. if (mPagedList != null) {
  3. return mPagedList.size();
  4. }
  5. return mSnapshot == null ? 0 : mSnapshot.size();
  6. }
  7. public T getItem(int index) {
  8. if (mPagedList == null) {
  9. if (mSnapshot == null) {
  10. throw new IndexOutOfBoundsException(
  11. "Item count is zero, getItem() call is invalid");
  12. } else {
  13. return mSnapshot.get(index);
  14. }
  15. }
  16. mPagedList.loadAround(index);
  17. return mPagedList.get(index);//从数据列表中获取数据
  18. }

        到这,首次刷新UI大家就能了解了,数据传递在AsyncPagedListDiffer此类中,有多少数据找它拿,然后我们在我们些的adapter的onBindViewHolder方法直接获取对应实例,直接赋值给控件即可。

        【3】后续的追加页面

        我们并写代码主动的追加,它是怎么实现的呢。其实看到刚才的getItem我们就已经看到了,它在获取Item时有调用PagedList的方法。

  1. public void loadAround(int index) {
  2. if (index < 0 || index >= size()) {
  3. //当前下标越界判断
  4. throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size());
  5. }
  6. mLastLoad = index + getPositionOffset();
  7. //主要加载方法
  8. loadAroundInternal(index);
  9. mLowestIndexAccessed = Math.min(mLowestIndexAccessed, index);
  10. mHighestIndexAccessed = Math.max(mHighestIndexAccessed, index);
  11. tryDispatchBoundaryCallbacks(true);
  12. }

       执行了loadAroundInternal方法,我们这是以连续源为例的,所以我们去ContiguousPagedList看

  1. protected void loadAroundInternal(int index) {
  2. int prependItems = getPrependItemsRequested(mConfig.prefetchDistance, index,
  3. mStorage.getLeadingNullCount());
  4. int appendItems = getAppendItemsRequested(mConfig.prefetchDistance, index,
  5. mStorage.getLeadingNullCount() + mStorage.getStorageCount());
  6. mPrependItemsRequested = Math.max(prependItems, mPrependItemsRequested);
  7. //判断是否为向前追加
  8. if (mPrependItemsRequested > 0) {
  9. schedulePrepend();
  10. }
  11. mAppendItemsRequested = Math.max(appendItems, mAppendItemsRequested);
  12. //判断是否为向后追加
  13. if (mAppendItemsRequested > 0) {
  14. scheduleAppend();
  15. }
  16. }

        我们这边就以往后追加看

  1. private void scheduleAppend() {
  2. if (mAppendWorkerState != READY_TO_FETCH) {
  3. return;
  4. }
  5. mAppendWorkerState = FETCHING;
  6. final int position = mStorage.getLeadingNullCount()
  7. + mStorage.getStorageCount() - 1 + mStorage.getPositionOffset();
  8. final V item = mStorage.getLastLoadedItem();
  9. mBackgroundThreadExecutor.execute(new Runnable() {
  10. @Override
  11. public void run() {
  12. if (isDetached()) {
  13. return;
  14. }
  15. if (mDataSource.isInvalid()) {
  16. detach();
  17. } else {
  18. //执行向后请求方法
  19. mDataSource.dispatchLoadAfter(position, item, mConfig.pageSize,
  20. mMainThreadExecutor, mReceiver);
  21. }
  22. }
  23. });
  24. }
  1. @Override
  2. final void dispatchLoadAfter(int currentEndIndex, @NonNull Value currentEndItem,
  3. int pageSize, @NonNull Executor mainThreadExecutor,
  4. @NonNull PageResult.Receiver<Value> receiver) {
  5. @Nullable Key key = getNextKey();
  6. if (key != null) {
  7. //这里就直接调用了自己实现的DataSource的loadAfter方法,给LoadCallbackImpl设置状态为APPEND
  8. loadAfter(new LoadParams<>(key, pageSize),
  9. new LoadCallbackImpl<>(this, PageResult.APPEND, mainThreadExecutor, receiver));
  10. } else {
  11. receiver.onPageResult(PageResult.APPEND, PageResult.<Value>getEmptyResult());
  12. }
  13. }
  1. @Override
  2. public void loadAfter(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, Person> callback) {
  3. List<Person> dataList = dataRepository.loadPageData(params.key, params.requestedLoadSize);
  4. if (dataList != null) {
  5. //数据交给回调
  6. callback.onResult(dataList, params.key + 1);
  7. }
  8. }
  1. @Override
  2. public void onResult(@NonNull List<Value> data, @Nullable Key adjacentPageKey) {
  3. if (!mCallbackHelper.dispatchInvalidResultIfInvalid()) {
  4. //判断type是往前追加,还是往后追加,设置对应的key
  5. if (mCallbackHelper.mResultType == PageResult.APPEND) {
  6. mDataSource.setNextKey(adjacentPageKey);
  7. } else {
  8. mDataSource.setPreviousKey(adjacentPageKey);
  9. }
  10. //最终还是调用到了dispatchResultToReceiver这个方法
  11. mCallbackHelper.dispatchResultToReceiver(new PageResult<>(data, 0, 0, 0));
  12. }
  13. }

        最后还是调用到了咱们的dispatchResultToReceiver方法,后续方法就不粘了,直接粘onPageResult方法。

  1. public void onPageResult(@PageResult.ResultType int resultType,
  2. @NonNull PageResult<V> pageResult) {
  3. ......
  4. List<V> page = pageResult.page;
  5. if (resultType == PageResult.INIT) {
  6. ......
  7. } else {
  8. boolean trimFromFront = mLastLoad > mStorage.getMiddleOfLoadedRange();
  9. boolean skipNewPage = mShouldTrim
  10. && mStorage.shouldPreTrimNewPage(
  11. mConfig.maxSize, mRequiredRemainder, page.size());
  12. if (resultType == PageResult.APPEND) {
  13. //状态为追加页面
  14. if (skipNewPage && !trimFromFront) {
  15. //没有增加任何数据
  16. mAppendItemsRequested = 0;
  17. mAppendWorkerState = READY_TO_FETCH;
  18. } else {
  19. //主要执行
  20. mStorage.appendPage(page, ContiguousPagedList.this);
  21. }
  22. } else if (resultType == PageResult.PREPEND) {
  23. if (skipNewPage && trimFromFront) {
  24. //没有增加任何数据
  25. mPrependItemsRequested = 0;
  26. mPrependWorkerState = READY_TO_FETCH;
  27. } else {
  28. //主要执行
  29. mStorage.prependPage(page, ContiguousPagedList.this);
  30. }
  31. } else {
  32. throw new IllegalArgumentException("unexpected resultType " + resultType);
  33. }
  34. ......
  35. }
  1. void appendPage(@NonNull List<T> page, @NonNull Callback callback) {
  2. final int count = page.size();
  3. ......
  4. mPages.add(page);
  5. mLoadedCount += count;
  6. mStorageCount += count;
  7. final int changedCount = Math.min(mTrailingNullCount, count);
  8. final int addedCount = count - changedCount;
  9. if (changedCount != 0) {
  10. mTrailingNullCount -= changedCount;
  11. }
  12. mNumberAppended += count;
  13. callback.onPageAppended(mLeadingNullCount + mStorageCount - count,
  14. changedCount, addedCount);
  15. }
  16. public void onPageAppended(int endPosition, int changedCount, int addedCount) {
  17. // consider whether to post more work, now that a page is fully appended
  18. mAppendItemsRequested = mAppendItemsRequested - changedCount - addedCount;
  19. mAppendWorkerState = READY_TO_FETCH;
  20. if (mAppendItemsRequested > 0) {
  21. // not done appending, keep going
  22. scheduleAppend();
  23. }
  24. // finally dispatch callbacks, after append may have already been scheduled
  25. notifyChanged(endPosition, changedCount);
  26. notifyInserted(endPosition + changedCount, addedCount);
  27. }

        这样就去刷新我们的数据了。但是我自己看的时候有疑问,后续的刷新啥时候有改动到mPagedList的大小。于是我跑到PagedList中看了这些方法。

  1. @Override
  2. public int size() {
  3. return mStorage.size();
  4. }
  5. public List<T> snapshot() {
  6. if (isImmutable()) {
  7. return this;
  8. }
  9. return new SnapshotPagedList<>(this);
  10. }
  11. @Override
  12. @Nullable
  13. public T get(int index) {
  14. T item = mStorage.get(index);
  15. if (item != null) {
  16. mLastItem = item;
  17. }
  18. return item;
  19. }

        数据它是存储在storage中的,所以这样我们就闭合了。

        总结:2.x-首先我们构建了一个LiveData然后在订阅的生命周期组件到达活跃时会触发去计算,计算中会通过我们实现的factory去创建我们的dataSource。接着创建PagedList中会执行我们dataSource的初始化请求,请求完毕后初始化storage,将数据存储。到此初始化和请求接口就走完了。然后postValue将我们的数据传递,主要实现是在父类的成员变量mDiffer中。adapter数据从这取。我们的数据存自storage。后续的追加页面由getItem来触发,后续数据增加storage的数据会改变,然后在通知adapter刷新。

        

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号