Paging2之PagedListAdapter 个人小结

来源:5-8 paging框架工作原理2【难点】

慕慕6500093

2022-11-18

三、PagedListAdapter

RecyclerView.Adapter base class for presenting paged data from PagedLists in a RecyclerView.This class is a convenience wrapper around AsyncPagedListDiffer that implements common default behavior for item counting, and listening to PagedList update callbacks.
它是用于在RecyclerView中显示PagedList中的分页数据的适配器基类,同时持有AsyncPagedListDiffer(前面讲过AsyncPagedListDiffer是一个将PagedList映射到RecyclerView.Adapter的Helper对象)对象并对其常用方法进行包装,使得它成为一个方便处理PagedList数据的适配器。

While using a LiveData<PagedList> is an easy way to provide data to the adapter, it isn't required - you can use submitList(PagedList) when new lists are available.
虽然使用LiveData<PagedList>是向适配器提供数据的一种简单方法,但这不是必需的——当有新列表可用时,可以使用submitList(PagedList)。

PagedListAdapter listens to PagedList loading callbacks as pages are loaded, and uses DiffUtil on a background thread to compute fine grained updates as new PagedLists are received.
PagedListAdapter在加载页面时侦听PagedList加载回调,并在收到新PagedList时在后台线程上使用DiffUtil计算细粒度更新。

Handles both the internal paging of the list as more data is loaded, and updates in the form of new PagedLists.
在加载更多数据时处理列表的内部分页,并以新PagedList的形式进行更新。

PagedListAdapter在构造时,需要传递一个DiffUtil.ItemCallback<T>参数,用于计算列表中两个非空项之间差异的回调。
protected PagedListAdapter(@NonNull DiffUtil.ItemCallback<T> diffCallback) {
        mDiffer = new AsyncPagedListDiffer<>(this, diffCallback);
        // 监听当前PagedList更新
        mDiffer.addPagedListListener(mListener);
}
要实现的DiffUtil.ItemCallback的2个方法:
// 调用以检查两个对象是否表示同一项,例如,如果您的项具有唯一的id,则判断id是否一致
areItemsTheSame(@NonNull T oldItem, @NonNull T newItem)
// 调用以检查两个项是否具有相同的数据。这就得重写equals方法了。
areContentsTheSame(@NonNull T oldItem, @NonNull T newItem)

由于PagedListAdapter继承的是RecycleView.Adapter, 同时它本是也个抽象了,所以子类XXPagedListAdapter需要实现的方法有:
// 创建 ViewHolder(负责承载每个item的布局)
// 当然也可以复写getItemViewType(position)来定义多种viewType
VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
// 绑定 ViewHolder (负责将每个item的holder绑定数据)
// 这里holder就是onCreateViewHolder拿到的VH(可以复用的对象),postion为当前PagedList的索引
void onBindViewHolder(@NonNull VH holder, int position) 

再来谈谈说上面说的submitList(pagedList)方法:
public void submitList(@Nullable final PagedList<T> pagedList) {
        submitList(pagedList, null);
}

// 将新的PagedList传递给AsyncPagedListDiffer对象。
// 如果PagedList已经存在,将在后台线程上异步计算diff。当计算diff时,它将被应用(调度到ListUpdateCallback),新的PagedList将作为当前列表交换。
public void submitList(@Nullable final PagedList<T> pagedList,
            @Nullable final Runnable commitCallback) {
        if (pagedList != null) {
             // 第一次提交
            if (mPagedList == null && mSnapshot == null) {
                mIsContiguous = pagedList.isContiguous();
            } else {
            // 必须保证每次提交的PagedList要么都是连续型的,要么都是非连续的。
                if (pagedList.isContiguous() != mIsContiguous) {
                    throw new IllegalArgumentException("AsyncPagedListDiffer cannot handle both"
                            + " contiguous and non-contiguous lists.");
                }
            }
        }

        // incrementing generation means any currently-running diffs are discarded when they finish
        final int runGeneration = ++mMaxScheduledGeneration;
        // 跟之前一样,啥也不做
        if (pagedList == mPagedList) {
            // nothing to do (Note - still had to inc generation, since may have ongoing work)
            if (commitCallback != null) {
                commitCallback.run();//不管有没有处理,都回传一下
            }
            return;
        }

       // mSnapshot可以理解为mPagedList的副本
        final PagedList<T> previous = (mSnapshot != null) ? mSnapshot : mPagedList;

        if (pagedList == null) {// 如果传入的为null
            int removedCount = getItemCount();
            if (mPagedList != null) {// 而之前的不为null,
               // 移除分页加载回调监听,
               // 这样后面就不会再触发onInserted(如:loadAfter)/onRemoved/onChanged这些方法执行了
                mPagedList.removeWeakCallback(mPagedListCallback);
                mPagedList = null;
            } else if (mSnapshot != null) {
                mSnapshot = null;
            }
            // 则移除之前加载的所有数据
            // dispatch update callback after updating mPagedList/mSnapshot
            mUpdateCallback.onRemoved(0, removedCount);
            // 并通知当前列表更新
            onCurrentListChanged(previous, null, commitCallback);
            return;
        }
        // pagedList有数据,而mPagedList没数据 副本也没有,说明是第一次
        if (mPagedList == null && mSnapshot == null) {
            // fast simple first insert
            mPagedList = pagedList;
            pagedList.addWeakCallback(null, mPagedListCallback);
            // adapter从第一行开始插入数据, 更新ui
            // dispatch update callback after updating mPagedList/mSnapshot
            mUpdateCallback.onInserted(0, pagedList.size());
            onCurrentListChanged(null, pagedList, commitCallback);
            return;
        }
        // pagedList有数据,mPagedList也有数据
        if (mPagedList != null) {
            // first update scheduled on this list, so capture mPages as a snapshot, removing
            // callbacks so we don't have resolve updates against a moving target
            mPagedList.removeWeakCallback(mPagedListCallback);
            // 因为需要在异步线程池中比较,所以先拿一个之前的副本
            mSnapshot = (PagedList<T>) mPagedList.snapshot();
            mPagedList = null;
        }

        if (mSnapshot == null || mPagedList != null) {
            throw new IllegalStateException("must be in snapshot state to diff");
        }

        final PagedList<T> oldSnapshot = mSnapshot;
        final PagedList<T> newSnapshot = (PagedList<T>) pagedList.snapshot();
        // 计算是否需要更新数据给adapter
        mConfig.getBackgroundThreadExecutor().execute(new Runnable() {
            @Override
            public void run() {
                final DiffUtil.DiffResult result;
                result = PagedStorageDiffHelper.computeDiff(
                        oldSnapshot.mStorage,
                        newSnapshot.mStorage,
                        mConfig.getDiffCallback());
                 ...
            }
        });
    }

这样就基本清楚了:
PagedList :存放来自DataSource数据的实体集合;
LiveData<PagedList> livedata:添加了监听数据能力;
xxViewModel.livedata:添加又添加了数据共享能力。
基本调用:xxViewModel.livedata.observe(viewLifecycleOwner) { pagedList -> submitList(pagedList) }

小结
1: 由LivePagedListBuilder.build的可知,DataSource是与PageList同时创建且相互关联的关系;
2: 又通过mRecycleView.adapter = pagedListAdapter, 再传给RecycleView,这样pagedListAdapter就与RecycleView进行了绑定;
3:最后 PagedListAdapter借助内部的AsyncPagedListDiffer来处理PagedList对象数据,而具体的数据data列表则由DataSource的loadXXX()接口被动触发获取并回传onResult(data列表)…最后由AsyncPagedListDiffer.mUpdateCallback触发ui更新。(透过源码可以看出,如果没有AsyncPagedListDiffer,这个PagedListAdapter就是一个普通的Adapter。)

借助AsyncPagedListDiffer&LivePagedListBuilder这两个纽带,就把PagedListAdapter、DataSource、PageList三者穿起来了。感觉Paging 组件就是谷歌用来设计给RecyclerView优化分页加载用的。

写回答

1回答

LovelyChubby

2022-11-22

你的逼格实在有点高,相当棒,给你点赞
0
0

开发商业级热门短视频App 掌握Jetpack组件库

Jetpack架构大揭秘,全组件实战主流且功能完整的短视频App

1364 学习 · 607 问题

查看课程