一、ListAdapter
当数据量不大时,咱们能够在UI线程中直接更新数据,可是当数据量大时这就比较为难了,咱们需求自己放在子线程操作,然后再回UI线程更新页面。在7.0上引进 DiffUtil 之后,现在 Google 又在最新的v7的27.1.1兼容包中加入官方的支持,便是 ListAdapter。
ListAdapter 是对 RecyclerView 传统 Adapter 的一个拓宽,在其内创立了 AsyncListDiffer 的示例,以便在后台线程中运用 DiffUtil 核算新旧数据集的差异。即在子线程中运用 DiffUtil 比较数据,并在 UI 线程更新。
public abstract class ListAdapter<T, VH extends RecyclerView.ViewHolder>
extends RecyclerView.Adapter<VH> {
private final AsyncListDiffer<T> mHelper;
@SuppressWarnings("unused")
protected ListAdapter(@NonNull DiffUtil.ItemCallback<T> diffCallback) {
mHelper = new AsyncListDiffer<>(new AdapterListUpdateCallback(this),
new AsyncDifferConfig.Builder<>(diffCallback).build());
}
@SuppressWarnings("unused")
protected ListAdapter(@NonNull AsyncDifferConfig<T> config) {
mHelper = new AsyncListDiffer<>(new AdapterListUpdateCallback(this), config);
}
/**
* Submits a new list to be diffed, and displayed.
* <p>
* If a list is already being displayed, a diff will be computed on a background thread, which
* will dispatch Adapter.notifyItem events on the main thread.
*
* @param list The new list to be displayed.
*/
@SuppressWarnings("WeakerAccess")
public void submitList(List<T> list) {
mHelper.submitList(list);
}
@SuppressWarnings("unused")
protected T getItem(int position) {
return mHelper.getCurrentList().get(position);
}
@Override
public int getItemCount() {
return mHelper.getCurrentList().size();
}
}
ListAdapter 用法跟一般的 RecyclerView 并无太大差别。
一共2个结构办法,差异在于第一个结构办法能够自己指定履行的线程
submitList(),用于提交新数据,更新UI
数据比较操作的线程在 AsyncListDiffer 中完成。需求注意的一点时,通过 AsyncListDiffer 回来的 List 是一个 UnmodifiableList,意味着不能改动长度。
二、AsyncListDiffer
前面咱们现已看到 DiffUtil 怎么帮忙 RecyclerView 进行UI更新。它有一个缺点便是 DiffUtil 在核算新旧数据集差异时需求开启线程,而在更新UI时又要在主线程。一般做法:
- Thread + Handler
- RxJava
尽管这样能够完成,但又显得蠢笨。在support-v7:27.1.0又新增了一个 DiffUtil 的封装类 – AsyncListDiffer,它在后台线程运用 DiffUtil 核算旧数据集->新数据集的最小量,一起又在主线程中将更新操作的最小量分发给 ListUpdateCallback,以完成数据更新,从而弥补 DiffUtil 的缺点。
DiffUtil.ItemCallback(需求晋级 RecyclerView 的 support 库)
和运用 DiffUtil 不同的是,咱们需求自界说承继自 ItemCallback 的类,而不是 Callback 类。与 DIffUtil.Callback 办法相似,DiffUtil 在核算旧数据集→新数据集时会回调它。在 DiffUtil.Callback 中,供给两种处理人物: Item 的列表索引和 Item 差异,而在 DiffUtil.ItemCallback 中只处理 Item 差异。
/**
* DiffUtil 在核算两个列表之间的非空 Item 的差异时运用的回调类。
* DiffUtil.Callback 供给两种处理人物 - 列表索引和 item 差异。而 DiffUtil.ItemCallback 只处理后者,它允许从UI和内容特定的差异代码中分离索引到数组或许 List 中。
*/
public abstract static class ItemCallback<T> {
/**
* 用来判别两个目标是否是相同的 Item
* 例如,假如你的 Item 有仅有的id字段,这个办法就判别 id 是否持平。
*/
public abstract boolean areItemsTheSame(T oldItem, T newItem);
/**
* 当它想要检查两个 Item 是否具有相同的数据时由 DiffUtil 调用。
* DiffUtil 运用回来的信息来判别内容是否已更改
* DiffUtil 用这个办法替代 equals 办法去检查是否持平,以便根据UI更改其行为
* 例如,假如你用 RecyclerView.Adapter 合作 DiffUtil 运用,需求回来 Item 的视觉表现是否相同。
* 这个办法仅仅在 areItemsTheSame() 回来true时,才调用。
*
* 回来true,表明新旧数据集中,当时方位的item内容相同,不然,不同
*/
public abstract boolean areContentsTheSame(T oldItem, T newItem);
/**
* 当 areItemsTheSame(int, int) 回来true而且 areContentsTheSame(int, int) 回来false时,DiffUtil 才会调用此办法,以获取该item改动的 payload
* 例如,假如你用 RecyclerView 合作 DiffUtils,你能够回来这个Item改动的那些字段,RecyclerView.ItemAnimator 能够运用哪些信息履行动画
* 默许的完成是回来null
* 回来一个代表着新老item的改动内容的 payload 目标
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public Object getChangePayload(T oldItem, T newItem) {
return null;
}
}
AsyncDifferConfig
AsyncDifferConfig 是 ListAdapter、AsyncListDiffer 和后台线程的配置目标。在其内,至少界说一个 DiffUtil.ItemCallback,用于 DiffUtil 在核算旧数据集->新数据集时回调。
AsyncDifferConfig.Builder
AsyncDifferConfig.Builder 是 AsyncDifferConfig 构建类,主要是用来配置 DiffUtil 核算数据集差异时的后台线程和 DiffUtil.ItemCallback 回调。
AsyncDifferConfig.Builder (DiffUtil.ItemCallback diffCallback)
在创立 AsyncDifferConfig.Builder 是有必要传递一个 DiffUtil.ItemCallback 目标,用于 DiffUtil 核算数据集差异时回调。当 AsyncDifferConfig.Builder 调用 build() 创立 AsyncDifferConfig 目标时,假如未设置指定的后台线程,那么将自动创立一个主线程 Executor 和长度为2的线程池。也便是说 AsyncDifferConfig 持有一个长度为2的线程池,由一切的 AsyncListDiffer 目标共用。假如想指定DiffUtil 核算的后台线程,可调用 setBackgroundThreadExecutor(Executor executor) 办法指定
每次在更新数据集时,都需求调用 AsyncListDiffer 的 submitList(List newList) 办法
1 public void submitList(final List<T> newList) {
// 2-5行:首要判别 newList 与 AsyncListDiffer 中缓存的数据集 mList 是否为同一个目标,假如是的话,直接回来。也便是说,调用 submitList() 办法所传递数据集时,需求new一个新的List。
2 if (newList == mList) {
3 // nothing to do
4 return;
5 }
6
7 // 界说了变量 runGeneration,用于缓存当时预履行线程的次数的最大值
8 final int runGeneration = ++mMaxScheduledGeneration;
9
// 10-16行:判别 newList 是否为null。若 newList 为 null,将移除一切 Item 的操作并分发给 ListUpdateCallback,mList 置为 null,一起将只读List - mReadOnlyList 清空
10 if (newList == null) {
11 // noinspection ConstantConditions
12 mUpdateCallback.onRemoved(0, mList.size());
13 mList = null;
14 mReadOnlyList = Collections.emptyList();
15 return;
16 }
17
// 18-24行:判别 mList 是否为null。若 mList 为null,表明这是第一次向 Adapter 增加数据集,此时将增加最新数据集操的作分发给 ListUpdateCallback,将 mList 设置为 newList,一起以 newList 为本来创立只读List副本 - mReadOnlyList
18 if (mList == null) {
19 // fast simple first insert
20 mUpdateCallback.onInserted(0, newList.size());
21 mList = newList;
22 mReadOnlyList = Collections.unmodifiableList(newList);
23 return;
24 }
25
26 final List<T> oldList = mList;
// 27-52行:syncDifferConfig 目标获取后台线程,并运行该后台线程。在该线程中,创立一个新的 DiffUtil.Callback() 回调,一起 DiffUtil 在核算旧数据集->新数据集的最小量时回调该 Callbcak。而这个 DiffUtil.Callback 在处理 Item 差异时,调用的是所传递过去的 DiffUtil.ItemCallback 实例中的 areItemsTheSame() 办法和areContentsTheSame()。这里需求注意的一点是:在这个 DiffUtil.Callback 中,并没有重写 getChangePayload() 办法,这不只意味着它并没有完成高效地部分绑定,一起自界说 DiffUtil.ItemCallback 时也没有必要完成 getChangePayload() 办法。
27 mConfig.getBackgroundThreadExecutor().execute(new Runnable() {
28 @Override
29 public void run() {
30 final DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() {
31 @Override
32 public int getOldListSize() {
33 return oldList.size();
34 }
35
36 @Override
37 public int getNewListSize() {
38 return newList.size();
39 }
40
41 @Override
42 public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
43 return mConfig.getDiffCallback().areItemsTheSame(
44 oldList.get(oldItemPosition), newList.get(newItemPosition));
45 }
46
47 @Override
48 public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
49 return mConfig.getDiffCallback().areContentsTheSame(
50 oldList.get(oldItemPosition), newList.get(newItemPosition));
51 }
52 });
53
// 54-63行:当 DiffUtil 核算新旧数据集差异之后,首要判别 runGeneration 与当时预履行线程的次数的最大值是否持平,假如持平,将 mList 设置为 newList,一起以 newList 为本来重建创立只读List副本 - mReadOnlyList,在主线程中将更新操作分发给 ListUpdateCallback。假如不持平,表明在此线程运行以后,再一次调用了submitList()办法用于传递新的数据集,此时将不做任何处理,待DiffUtil核算结束以后,再做相应处理。
54 mConfig.getMainThreadExecutor().execute(new Runnable() {
55 @Override
56 public void run() {
57 if (mMaxScheduledGeneration == runGeneration) {
58 latchList(newList, result);
59 }
60 }
61 });
62 }
63 });
64 }
65 private void latchList(@NonNull List<T> newList, @NonNull DiffUtil.DiffResult diffResult) {
66 mList = newList;
67 mReadOnlyList = Collections.unmodifiableList(newList);
68 diffResult.dispatchUpdatesTo(mUpdateCallback);
69 }
值得注意的两点便是:
- 调用 submitList() 办法传递数据集时,需求new一个新的List。
- 在这个 DiffUtil.Callback 中,并没有重写 getChangePayload() 办法,这不只意味着它并没有完成高效地部分绑定,一起自界说 DiffUtil.ItemCallback 时也没有必要完成 getChangePayload() 办法。
了解了 submitList() 办法的完成方式,假如对没有完成高效地部分绑定的问题,完全能够自界说 AsyncListDiffer,然后在创立 DiffUtil.Callback() 回调时,重写 getChangePayload() 办法即可。当然在 Apdaer 中也需求重写 onBindViewHolder(holder: DiffViewHolder, position: Int, payloads: MutableList) 办法。
参阅链接:
blog.csdn.net/IO_Field/ar…
developer.android.com/reference/a…
developer.android.google.cn/reference/a…
——乐于共享,共同进步,欢迎留言评论
——Treat Warnings As Errors
——Any comments greatly appreciated
——Talking is cheap, show me the code
——CSDN:blog.csdn.net/u011489043
——GitHub:github.com/selfconzrr