RecyclerView를 업데이트 하기 위해 초기에는 notifyDataSetChanged()를 호출하여 전체 목록을 업데이트 했지만
이러한 방법은 전체 목록을 재생성하기 때문에 매우 비효율적이다.
이러한 문제를 해결하기 위해 등장한 DiffUtil은
데이터 세트 간의 차이점을 계산하여 필요한 최소한의 업데이트만을 수행함으로써 성능을 개선하는 것이 목적이다.
즉, 두 목록간의 차이점을 찾고 업데이트 되어야 할 목록들을 반환하고RecyclerView 어댑터에 대한 업데이트를 알리는데 사용된다.
DiffUtil을 사용해야 하는 상황에 대한 예시
블로그를 작성하는 과정에서 사용자가 새로고침을 하여 블로그 게시글이 업데이트 되었다고 가정해보자
초기 데이터 세트
Post A
Post B
Post c
변경된 데이터 세트
Post B (업데이트 됨)
Post D ( 신규 추가 )
Post A ( 순서 변경 )
위의 변경사항을 RecyclerView를 업데이트하는 기존의 메소드(notifyItemChanged()...)로 처리하면
다음과 같은 단계를 수행해야 한다.
- Post C가 삭제되었음을 감지하고 notifyItemRemoved(2)를 호출
- Post D가 추가되었음을 감지하고 notifyItemInserted(1)을 호출
- Post A와 Post B의 위치가 변경되었음을 감지하고, 이에 대해 notifyItemMoved()를 호출
- Post B가 업데이트되었음을 감지하고 notifyItemChanged(0)을 호출
이 모든 사항을 수동으로 추적하고 관리하기에는 개발자의 부담이 너무나도 크다
DiffUtil 클래스는 두 데이터 세트 간의 차이점을 효율적이게 계산하고 최소한의 업데이트를 한다.
DiffUtil은 변경된 항목만을 자동으로 감지하고, 이러한 변경 사항을 RecyclerView에 알려주므로, 개발자가 수동으로 변경 사항을 추적할 필요가 없다.
ㄹ
DiffUtil을 사용법
DiffUtil.Callback은 DiffUtil을 사용할 때 핵심적인 추상 클래스로 두 목록 간의 차이를 계산하는 동안 DiffUtil에 의해 사용되는 콜백 클래스 이다.
개발자는 DiffUtil.CallBack을 상속받아 4개의 추상 메서드와 선택적인 1개의 메서드를 구현하여야 한다.
- getOldListSize() : 이전 데이터 세트의 크기를 반환
- getNewListSize() : 새로운 데이터 세트의 크기를 반환
- areItemsTheSame(int , int) : 두 데이터 세트에서 같은 위치에 있는 두 항목이 동일한 항목인지 비교
=> 주로 항목의 고유 식별자를 비교 - arContentsTheSame(int , int) : 3번에서 동일하다고 판단된 두 항목의 내용이 같은지 비교
=> 내용이 다르면 업데이트 되었다고 판단 가능 / 다르면 true , 같으면 false 반환 - getChangePayload(int , int) (선택적) : 항목이 같지만 내용이 변경된 경우, 변경된 내용의 세부 정보나 payload 제공
=> 3번이 true, 4번이 false를 반환하는 경우 / 뷰의 특정 부분만 업데이트 하는 데 사용
List<YourDataType> oldList = // 이전 데이터 세트
List<YourDataType> newList = // 새로운 데이터 세트
DiffUtil.DiffResult diffResult = 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) {
// 여기서는 항목의 고유 ID 등을 비교
return oldList.get(oldItemPosition).getId().equals(newList.get(newItemPosition).getId());
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
// 항목의 내용이 같은지 비교
return oldList.get(oldItemPosition).equals(newList.get(newItemPosition));
}
@Nullable
@Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
// 변경된 항목의 설명이나 상태 등, 변경된 내용을 담은 객체를 생성
Bundle diffBundle = new Bundle();
if (diffBundle.size() == 0) return null; // 변경 사항이 없다면 null 반환
return diffBundle; // 변경 사항이 담긴 객체 반환
}
});
diffResult.dispatchUpdatesTo(adapter); // adapter는 RecyclerView.Adapter의 인스턴스
UI 블로킹
안드로이드에서 메인 스레드(UI 스레드)는 사용자 인터페이스와 관련된 작업을 처리한다.
이 스레드에서 시간이 많이 소요되는 작업을 수행하면, 앱의 반응성이 떨어지고 사용자 경험이 저하된다.
특히 DiffUtil의 계산 과정이 복잡하고 시간이 많이 걸리는 경우, 메인 스레드에서 작업을 수행한다면 앱이 일시적으로 멈추는 현상이 발생할 수 있다.
그렇기에 DiffUtil 연산은 백그라운드 스레드에서 수행하는 것이 권장되며 이를 위해 AsycListDiffer 또는 ListAdapter와 같은 추상화 도구를 사용하는 것이 더 효율적이다.
다음은 이어서 AsycListDiffer를 알아보자
https://ehdnsdlek.tistory.com/21
[Android] RecyclerView의 AsyncListDiffer 알아보기
DiffUtil에 대한 정보는 이전에 포스팅 하였다. 오늘은 AsyncListDiffer에 대해 알아보자 AsyncListDiffer DiffUtil은 메인 스레드에서 작업이 수행되는 경우 성능이 매우 저하된다고 했다. AsyncListDiffer는 DiffUt
ehdnsdlek.tistory.com
'Android' 카테고리의 다른 글
[Android] RecyclerView와 ListAdapter (1) | 2024.03.08 |
---|---|
[Android] RecyclerView의 AsyncListDiffer 알아보기 (0) | 2024.03.07 |
[Android] absoluteAdapterPosition 와 getBindingAdapterPosition 차이점 (0) | 2024.03.04 |
[Android] ViewHolder의 위치 참조 - getAdapterPosition / getBindingAdapterPosition (0) | 2024.03.03 |
[Android] Fragment 생성 방법 정리 / newInstance() (0) | 2024.03.02 |