再探DiffUtil

200 阅读3分钟

DiffUtil

______________step1__________提交数据列表___________

/**
* @param 新数据列表 
* @param 差异计算完毕回调 Runnable类型
*/
public void submitList(@Nullable final List<T> newList,  
@Nullable final Runnable commitCallback);

public void submitList(@Nullable final List<T> newList) {  
            submitList(newList, null);  
}

_______________step2__________差异计算过程___________

public void submitList(@Nullable final List<T> newList,  
@Nullable final Runnable commitCallback) {  

/** 
* @param 每次调用 submitList mMaxScheduledGeneration递增即为一次,只有当差异计算完毕时,当前计数器和提交时的计数器是同一个才视为正确的计算结果,否则视为垃圾数据,等待下一次的计算完成再更新
*/
final int runGeneration = ++mMaxScheduledGeneration;  
  
//如果数据列表对象是和上一次提交的是同一个,则不会计算,这里有个坑就是对象的地址不随对象内容的更改而改变,即使你add了一个数据都会视为同一个对象而不进行更改,所以每次提交都需要new一个新的List出来
if (newList == mList) {  
if (commitCallback != null) {  
commitCallback.run();  
}  
return;  
}  
  
//previousList旧数据只读列表
final List<T> previousList = mReadOnlyList;  
  
//新数据为null,视为清除所有数据,也不用计算诧差异了,直接通知全部移除
if (newList == null) {  

int countRemoved = mList.size();  
mList = null;  
mReadOnlyList = Collections.emptyList();  

mUpdateCallback.onRemoved(0, countRemoved);  //通知,会调用mAdapter.notifyItemRangeRemoved(position, count);
onCurrentListChanged(previousList, commitCallback);  //回调ListAdapter里的onCurrentListChanged,ListAdapter里的onCurrentListChanged默认是空方法,若要收到通知,需要继承实现ListAdapter的同名方法。
return;  
}  
  
//旧数据为null 说明是第一次提交List数据,也不用计算差异
if (mList == null) {  
mList = newList;  
mReadOnlyList = Collections.unmodifiableList(newList);  

mUpdateCallback.onInserted(0, newList.size());  //通知Adapter
onCurrentListChanged(previousList, commitCallback);  
return;  
}  

//oldList旧数据列表,目前和previousList内容一样
final List<T> oldList = mList;  

//后台计算差异
mConfig.getBackgroundThreadExecutor().execute(new Runnable() {  
@Override  
public void run() {  

//后台计算差异
final DiffUtil.DiffResult result = DiffUtil.calculateDiff(......);  
  
//主线程提交结果
mMainThreadExecutor.execute(new Runnable() {  
@Override  
public void run() {  
if (mMaxScheduledGeneration == runGeneration) {  
latchList(newList, result, commitCallback);  
}  
}  
});  
}  
});  
}

__step3_DiffUtil.calculateDiff如何使用DiffUtil.Callback

public static DiffResult calculateDiff(@NonNull Callback cb, boolean detectMoves) {  
final int oldSize = cb.getOldListSize();  
final int newSize = cb.getNewListSize();  
final Snake snake = midPoint(range, cb, forward, backward);//调用areItemTheSame
..............

return new DiffResult(cb, diagonals,  
forward.backingData(), backward.backingData(),  
detectMoves);  
}

new DiffResult

DiffResult(Callback callback, List<Diagonal> diagonals, int[] oldItemStatuses,  
int[] newItemStatuses, boolean detectMoves) {  
........
mCallback = callback;  
mOldListSize = callback.getOldListSize();  
mNewListSize = callback.getNewListSize();  
........
findMatchingItems();  
}

调用mCallback.areContentsTheSame给Item做标记

FLAG_MOVED 移动 对应于 adapter.onMoved 非FLAG_MOVED对应于onInsert和onRemove,这里还没有看懂先mark住

FLAG_CHANGED 更新 对应于 adapter.onChange


private void findMatchingItems() {  
for (Diagonal diagonal : mDiagonals) {  
for (int offset = 0; offset < diagonal.size; offset++) {  
int posX = diagonal.x + offset;  
int posY = diagonal.y + offset;  
final boolean theSame = mCallback.areContentsTheSame(posX, posY);  //先比较areContentsTheSame,如果返回true,标记此位置不需要更新,返回false标记此位置需要更新
final int changeFlag = theSame ? FLAG_NOT_CHANGED : FLAG_CHANGED;  
mOldItemStatuses[posX] = (posY << FLAG_OFFSET) | changeFlag;  
mNewItemStatuses[posY] = (posX << FLAG_OFFSET) | changeFlag;  
      }  
  }  
//mDetectMoves一直为true 检测移动的项目
if (mDetectMoves) {  
      findMoveMatches();  
   }  
}

findMoveMatches


private void findMoveMatches() {  
int posX = 0;  
for (Diagonal diagonal : mDiagonals) {  
while (posX < diagonal.x) {  
if (mOldItemStatuses[posX] == 0) { findMatchingAddition(posX); }  
       posX++;  
      }   
 posX = diagonal.endX();
    }  
}

findMatchingAddition


private void findMatchingAddition(int posX) {  
int posY = 0;  
final int diagonalsSize = mDiagonals.size();  
for (int i = 0; i < diagonalsSize; i++) {  
final Diagonal diagonal = mDiagonals.get(i);  
while (posY < diagonal.y) {  
// found some additions, evaluate  
if (mNewItemStatuses[posY] == 0) { // not evaluated yet  
boolean matching = mCallback.areItemsTheSame(posX, posY);  
if (matching) {  
// yay found it, set values  
boolean contentsMatching = mCallback.areContentsTheSame(posX, posY);  
final int changeFlag = contentsMatching ? FLAG_MOVED_NOT_CHANGED  
: FLAG_MOVED_CHANGED;  
// once we process one of these, it will mark the other one as ignored.  
mOldItemStatuses[posX] = (posY << FLAG_OFFSET) | changeFlag;  
mNewItemStatuses[posY] = (posX << FLAG_OFFSET) | changeFlag;  
return;  
}  
}  
posY++;  
}  
posY = diagonal.endY();  
}  
}

areItemsTheSame false:判断从何处开始更新,哪些是移除,哪些是新增的 areContentsTheSame true判断哪个需要更新,areItemsTheSame返回true时,则使用OnMove来更新

如果某个位置需要更新,则areItemsTheSame必返回false