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