[안드로이드] DiffUtil
기존 notifyDataChanged()의 단점
리사이클러뷰를 사용할 때 아이템 뷰의 데이터가 변경되어서 업데이트를 해야 하는 경우 notifyDataChanged() 함수를 통해 리사이클러뷰에게 알리게 된다.
notifyDataChanged() 함수는 업데이트를 할 때 모든 아이템 뷰의 데이터들을 업데이트를 하기 때문에 적은 개수의 아이템을 업데이트하는 경우 비효율적이다.
Diffutil
Diffutil은 olditems와 newitems의 차이를 계산해서 업데이트해야 하는 아이템들만 변경하게 된다.
추상 클래스인 DiffUtil.Callback을 사용해서 구현을 하게 된다.
4가지의 추상 메서드와 1가지의 비 추상 메서드를 가지고 있다.
- getOldListSize() : 바뀌기 전 리스트의 크기를 반환합니다.
- getNewListSize() : 바뀐 후 리스트의 크기를 반환합니다.
- areItemsTheSame(oldPosition:Int, newPosition:Int) : 두 객체가 동일한 항목을 나타내는지 확인합니다. 만일 true라면 다음 비교를 하고, false라면 리스트 갱신 시 화면이 깜빡거리는 현상이 발생할 수 있습니다. 즉, areItemsTheSame을 잘못 정의한다면 다시 새로 만들게 되어서 notifyDataSerChanged()와 다를 바 없이 집니다.
- areContentsTheSame(oldPosition:Int, newPosition:Int) : 두 항목의 데이터가 같은지 확인한다. 해당 메서드는 areItemsTheSame()에서 true 인 경우에만 호출합니다. 같은 id값을 가졌더라도 내부의 값이 달려졌다면 변경된 것이므로 그것을 확인해야 한다. 최종적으로 false인 item에 대해서만 onBindViewHolder 메서드가 호출됩니다.
AsyncListDiffer
아이템 수가 많은 경우 olditems와 newitems의 차이를 계산을 해야 하는 시간이 오래 거릴 수 있다. 그래서 AsyncListDiffer가 등장했다. 백그라운드에서 diffutil을 사용할 수 있게 helper 클래스이다.
리사이클러뷰를 업데이트하기위해 submitList() 메서드를 호출한다. submitList를 통해 넘어온 리스트가 이전 리스트와 같다면
리스트를 변경하지않고 그대로 종료한다. (아이템이 없었는데 새로 생겼거나, 반대로 아이템이 있었는데 없어진 경우 내부적으로 콜백리스너(ListUpdateListener)를 통해 RecyclerView.Adapter 의 notifyItemRangeInserted(), notifyItemRangeRemoved() 함수가 호출) 다른 리스트를 전달할 경우 백그라운드에서 이전 리스트와 새로운 리스트의 차이를 연산하게 되는데 이때 difftuil.callback() 함수를 사용한다.
연산을 한 후 Main Thread에서 dispatchUpdatesTo에 넘겨줌으로써 RecyclerView를 update한다.
// AsyncListDiffer.java
final List<T> oldList = mList;
// background thread에서 calculateDiff를 수행
mConfig.getBackgroundThreadExecutor().execute(new Runnable() {
@Override
public void run() {
final DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() { ... });
// 차이를 계산하여 main thread에서 update
mMainThreadExecutor.execute(new Runnable() {
@Override
public void run() {
if (mMaxScheduledGeneration == runGeneration) {
latchList(newList, result, commitCallback);
}
}
});
}
});
@SuppressWarnings("WeakerAccess") /* synthetic access */
void latchList(
@NonNull List<T> newList,
@NonNull DiffUtil.DiffResult diffResult,
@Nullable Runnable commitCallback) {
final List<T> previousList = mReadOnlyList;
mList = newList;
// notify last, after list is updated
mReadOnlyList = Collections.unmodifiableList(newList);
diffResult.dispatchUpdatesTo(mUpdateCallback);
onCurrentListChanged(previousList, commitCallback);
}
ListAdapter
AsyncListDiffer를 좀 더 편하게 사용할 수 있도록 해주는 래퍼클래스이다.
ListAdapter는 DiffUtil을 활용하여 리스트를 업데이트하는 기능이 추가된 Adapter이다.
Reference
https://velog.io/@errored_pasta/Android-ListAdapter
[Android] AsyncListDiffer와 ListAdapter
RecyclerView를 사용할 때, 리스트가 update되어 데이터를 다시 출력해야할 경우 adapter의 notifyDateSetChanged()를 호출하여 사용합니다. 하지만 이는 성능에 좋지 않은 영향을 미치게 됩니다. notifyDataSetChan
velog.io
https://velog.io/@hyeryeong/Android-RecyclerView3-DiffUtil
[Android] RecyclerView(3) - DiffUtil
우리는 리스트를 나타내기 위해서 RecyclerView를 사용합니다. 그리고 리스트의 데이터에 변화가 있으면 notifyDataChanged() 메서드를 사용해서 알리게 됩니다.하지만 notifyDataChanged 메서드는 새로운 item
velog.io