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,讓其實現局部刷新。
到了這裏,我要講的就差很少要結束了,但願對大家有所幫助。謝謝大家看到了這裏。