RecyclerView的出现让咱们能够实现更多更复杂的滑动布局,包括不一样的布局类型,不一样的数据类型。可是,越是复杂的布局,出现卡顿的现象就会愈加的明显。git
这其中不乏有如下几点:github
经过滑动的日志分析,咱们能够发现同一模版在上滑下滑的同时,会从新走onBindView
方法,即便这一模版内容没有任何变化的状况下。若是在这个方法中所要执行的逻辑不少,这将会致使卡顿的出现。缓存
那么为什么会从新走onBindView
方法呢,你可能会说去看源码就知道了呀。没错,当你不知道它是如何实现的时候,去看源码每每是最直接有效的。可是今天这个并非这篇文章的重点,关于RecyclerView的复用和回收网上有不少源码的解析,这里就不一一贴源码解释了,只是作一些简单的介绍。bash
ViewHolder
而不是View。mChangedScrap
、mCacheViews
、开发者自定义以及 RecycledViewPool
中,都没有才会onCreatViewHolder
。RecyclerViewPool
中的存储方式是 viewType-Array,也就是对对于每种类型最多存5个。大部分的缓存是从recyclerViewPool中拿的,recyclerViewPool必定会走onBindViewHolder
方法。这也就是回答了咱们上面的提问,因此咱们的思路就来了,能够经过判断数据的变化来控制onBindView中相应逻辑的执行,来提高性能。异步
DiffUtil
主要是和RecyclerView或者ListView配合使用,由DiffUtil找出每一个item的变化,再由RecyclerView。Adapter来更新UI。ide
此次优化的思路就是在onBindviewHolder中判断新旧item的变化,来作到精准更新。布局
更新UI必需要在主线程中,可是DiffUtil是一个耗时的操做,因此此次用的是它的一个封装类AsyncListDifferConfig
性能
首先,在初始化中新建Differ对象。优化
private final IDataDiff mDataDiff;
private final AsyncListDifferDelegate<DATA> mDiffer;
private final IDataCache<DATA> dataElementCache;
public BaseSwiftAdapter(Context mContext) {
this.mContext = mContext;
dataElementCache = new ElementCache<>();
final DiffCallBack diffCallBack = new DiffCallBack(dataElementCache);
@SuppressLint("RestrictedApi") AsyncDifferConfig config =
new AsyncDifferConfig.Builder<>(diffCallBack)
.setBackgroundThreadExecutor(AppExecutors.backGroudExecutors)
.setMainThreadExecutor(AppExecutors.mainExecutors)
.build();
ChangeListCallback changedPositionCallback = new ChangeListCallback(this);
mDataDiff = new DataDiffImpl<>(changedPositionCallback, dataElementCache);
mDiffer =
new AsyncListDifferDelegate(changedPositionCallback, config, dataElementCache);
}
复制代码
AsyncListDifferConfig
须要三个参数:DiffUtil的内部类ItemCallback、diffUtil的item比较线程、主线程。ui
ItemCallback
是它的抽象内部类,看下它要实现的几个方法:
@Override
public boolean areItemsTheSame(IElement oldItem, IElement newItem) {
return areContentsTheSame(oldItem, newItem);
}
@Override
public boolean areContentsTheSame(IElement oldItem, IElement newItem) {
if (newItem == null) {
return true;
}
if (oldItem == newItem) {
return true;
}
recordNewElement(newItem);
final String newContent = newItem.diffContent();
if(newContent == null || "".equals(newContent)){
return false;
}
return newContent.equals(oldItem.diffContent());
}
复制代码
areItemTheSame和areContentsTheSame,都是用来判断新旧数据是否相同,因此这里用了同一个逻辑:先比较对象再比较关键字段,diffContent中存放该数据具备影响的几个字段相拼接的字符串。
dataElementCache用来存储全部数据的集合类型是IElement-ElementRecord的Array。IElement是数据自己,ElementRecord是数据的记录集,包含数据以及数据的惟一标示。
mDataDiff以及mDiffer会在后续中讲到。
咱们来看下关键的onBindViewHolder
中所作的事情:
@Override
public final void onBindViewHolder(VH holder, int position) {
if (null != holder && holder.itemView != null) {
onBindData(holder, position, this.getItem(position));
}
}
private void onBindData(VH holder, int position, DATA newData) {
final ElementRecord oldDataRecord = holder.content();
boolean needBind ;
if(needBind = (hasPositionDataRefreshChanged(oldDataRecord == null ? null : (DATA) oldDataRecord.getElement(), newData, position) || oldDataRecord == null) ){
Log.d(getClass().getName(),"adapter onBindData 刷新或者新建"+ holder.getItemViewType());
}else if(needBind = hasDataContentChanged(oldDataRecord,newData)){
Log.d(getClass().getName(),"adapter onBindData 滑动内容改变"+ holder.getItemViewType());
}
if(needBind){
refreshAndBind(holder, position, newData);
}else {
Log.d(getClass().getName(),"adapter onBindData 复用不刷新"+ holder.getItemViewType());
}
}
复制代码
先去判断是不是刷新变化,其次去判断是不是滑动变化,若是有变化就刷新布局,不然什么也不作。
private boolean hasPositionDataRefreshChanged(DATA oldItem, DATA newItem, int position){
return mDataDiff.areItemsChanged(oldItem, newItem, position);
}
private boolean hasDataContentChanged(ElementRecord oldItem, DATA newItem){
return mDataDiff.areContentsChanged(oldItem, newItem);
}
复制代码
能够看出mDataDiff
主要用来判断新旧数据是否相同。
其中比较思路为:先判断该viewHolder是否在changedPositions中,changedPositions由ChangeListCallback来提供并实现。其次判断两个对象以及惟一标示。
/**
* 刷新列表
*
* @param pagedList 新的列表数据
*/
public final void refreshDataSource(List<DATA> pagedList) {
mDiffer.submitList(pagedList);
}
复制代码
全部的刷新都要走这个方法。
/**
* 比较数据差别,分发差别结果,调用局部刷新API。
* @param newList 新的数据源
*/
public void submitList(final List<T> newList) {
if (newList == mList) {
tryNotity();
return;
}
final int runGeneration = ++mMaxScheduledGeneration;
// 若是新集合是空 就把老集合全部都remove
if (newList == null) {
int countRemoved = mList.size();
mList = null;
mUpdateCallback.onRemoved(0, countRemoved);
return;
}
// 若是老集合是空 就把新集合全部都insert
if (mList == null) {
mList = newList;
updateDataSource(Collections.unmodifiableList(newList));
mConfig.getBackgroundThreadExecutor()
.execute(
new Runnable() {
@SuppressLint("RestrictedApi")
@Override
public void run() {
for (int i = 0; i < newList.size(); i++) {
final T t = newList.get(i);
if(t!=null){
dataElementCache.putRecord(new ElementRecord(IDHelper.getUniqueId(t),t));
}
}
dataElementCache.copySelf();
}
});
mUpdateCallback.onInserted(0, newList.size());
return;
}
final List<T> oldList = mList;
mConfig.getBackgroundThreadExecutor().execute(new Runnable() {
@SuppressLint("RestrictedApi")
@Override
public void run() {
final DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() {
@Override
public int getOldListSize() {
return oldList.size();
}
@Override
public int getNewListSize() {
return newList.size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return mConfig.getDiffCallback().areItemsTheSame(
oldList.get(oldItemPosition), newList.get(newItemPosition));
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return mConfig.getDiffCallback().areContentsTheSame(
oldList.get(oldItemPosition), newList.get(newItemPosition));
}
// payload能够理解为关键的数据,就是新老item的数据中 到底哪里变化了,局部刷新某个item -- 默认返回null
@Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
return mConfig.getDiffCallback().getChangePayload(
oldList.get(oldItemPosition), newList.get(newItemPosition));
}
});
mConfig.getMainThreadExecutor().execute(new Runnable() {
@Override
public void run() {
if (mMaxScheduledGeneration == runGeneration) {
//刷新布局
diffResult.dispatchUpdatesTo(mUpdateCallback);
}
}
});
}
});
}
复制代码
这里提供了异步进行数据比较的逻辑,mUpdateCallback就是ChangeListCallback,实现ListAdapterListUpdateCallback接口,实现adpter的刷新功能。
最关键的代码在这句:
diffResult.dispatchUpdatesTo(mUpdateCallback);
复制代码
diffResult会将最小变化量提供给adpter,让其实现局部刷新。
到了这里,我要讲的就差很少要结束了,但愿对大家有所帮助。谢谢大家看到了这里。