Android DiffUtil
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android DiffUtil相关的知识,希望对你有一定的参考价值。
android 的recyclerview-v7:24.2.0 发布后多了个DiffUtil工具类,这个工具类能够大大解放了Android开发者的一个苦恼:RecyclerView局部刷新和重新刷新时实际只改变了部分数据。
DiffUtil能够计算两个列表之间的差值,并计算出旧列表变换到新列表的过程(DiffResult),DiffResult可以直接应用到RecyclerView的Adapter中,DiffResult会使用Adapter的notifyItemRangeChanged等方法来更新RecyclerView。
DiffUtil使用了Eugene W.Myers‘s difference algorithm 去计算从旧集合到新集合的最小更新序列,Myers 算法并没有处理集合中的子项偏移情况,所以如果选择了要计算偏移情况的话,需要进行第二次的运算来检查子项偏移,所以会产生更多的计算时间成本。当集合的数据量较大时,Myers算法的处理时间会较长,所以不要在集合数据量大时在主线程进行Diff运算,应该放在后台线程中进行运算,将运算结果(DiffResult)在主线程进行更新RecyclerView。需要值得注意的是,子项偏移的运算会比较明显得增加Diff时间,所以在集合默认是排序情况下,关闭该功能。
DiffUtil的Demo可以在网络上其他地方找到,这里不再赘述,只简单提下大致使用方法。
1、calculateDiff是最核心的方法,Callback并不是我们常见的回调,更准确点说应该是一个策略
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(DiffUtil.Callback, boolean);
2、DiffUtil.Callback可实现的方法,DiffUtil会对每个position调用对应的方法,我们只需要在Callback中告诉DiffUtil,在哪个位置中,如何判别两个列表在这个位置是否是同一个子项,如果是,有没有局部变化,如果有局部变化,都变了哪里?DiffUtil只需要知道这些,就可以计算出DiffResult
/** * A Callback class used by DiffUtil while calculating the diff between two lists. */ public abstract static class Callback { /** * Returns the size of the old list. * * @return The size of the old list. */ public abstract int getOldListSize(); /** * Returns the size of the new list. * * @return The size of the new list. */ public abstract int getNewListSize(); /** * Called by the DiffUtil to decide whether two object represent the same Item. * <p> * For example, if your items have unique ids, this method should check their id equality. * * @param oldItemPosition The position of the item in the old list * @param newItemPosition The position of the item in the new list * @return True if the two items represent the same object or false if they are different. */ public abstract boolean areItemsTheSame(int oldItemPosition, int newItemPosition); /** * Called by the DiffUtil when it wants to check whether two items have the same data. * DiffUtil uses this information to detect if the contents of an item has changed. * <p> * DiffUtil uses this method to check equality instead of {@link Object#equals(Object)} * so that you can change its behavior depending on your UI. * For example, if you are using DiffUtil with a * {@link android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}, you should * return whether the items‘ visual representations are the same. * <p> * This method is called only if {@link #areItemsTheSame(int, int)} returns * {@code true} for these items. * * @param oldItemPosition The position of the item in the old list * @param newItemPosition The position of the item in the new list which replaces the * oldItem * @return True if the contents of the items are the same or false if they are different. */ public abstract boolean areContentsTheSame(int oldItemPosition, int newItemPosition); /** * When {@link #areItemsTheSame(int, int)} returns {@code true} for two items and * {@link #areContentsTheSame(int, int)} returns false for them, DiffUtil * calls this method to get a payload about the change. * <p> * For example, if you are using DiffUtil with {@link RecyclerView}, you can return the * particular field that changed in the item and your * {@link android.support.v7.widget.RecyclerView.ItemAnimator ItemAnimator} can use that * information to run the correct animation. * <p> * Default implementation returns {@code null}. * * @param oldItemPosition The position of the item in the old list * @param newItemPosition The position of the item in the new list * * @return A payload object that represents the change between the two items. */ @Nullable public Object getChangePayload(int oldItemPosition, int newItemPosition) { return null; } }
3、DiffResult应用到RecyclerView的Adapter.
DiffResult.dispatchUpdatesTo(RecyclerView.Adapter);
4、关于Adapter以前的一个知识点,payloads,可能有些朋友还不知道Adapter还有一个onBindViewHolder重载方法,比常见的onBindViewHolder多一个参数:List<Object> payloads。当DiffResult中有某一项出现两个列表实际上在该位置是同一个子项,但是发生了局部变化,则调用该重载方法时带上payloads,payloads中的对象可以在Callback的getChangePayload给定。payloads不会为null,所以检查payloads是否为空,为空,直接调用默认的onBindViewHolder即可。
public void onBindViewHolder(StudentViewHolder holder, int position, List<Object> payloads) {}
以上是关于Android DiffUtil的主要内容,如果未能解决你的问题,请参考以下文章
Android 高性能列表:RecyclerView + DiffUtil
DiffUtil 和 registerAdapterDataObserver
Android的Kotlin秘方(II):RecyclerView 和 DiffUtil