본문 바로가기
android

[Kotlin] DiffUtil

by liz_devel 2021. 9. 8.

Diffutil이란

기존 list와 새 list를 비교해서 변경된 아이템만 골라 변경해주는 유틸리티 클래스입니다.

BaseAdapter에서 자주 사용하던 notifyDataSetChanged()라는 리스트를 업데이트해 주는 메서드가 있지만 하나의 아이템이 변경되어도 그 아이템만 변경해주는 게 아니라 모든 뷰를 전체적으로 다시 그려줘야해서 비용이 많이 들었습니다.

물론 BaseAdapter에 아이템만 변경해주는 notifyItemChanged(int position)라는 메서드도 있습니다.

하지만 이 메서드는 포지션값을 알고 있어야 하며 하나의 아이템만 변경해줍니다.

 

notifyDataSetChanged()
연결된 관찰자에게 기본 데이터가 변경되었으며 데이터 세트를 반영하는 모든 뷰가 자체적으로 새로 고쳐야 함을 알립니다.

출처 https://developer.android.com/reference/android/widget/BaseAdapter

notifyItemChanged(int position)
등록된 관찰자에게 의 항목 position이 변경 되었음을 알립니다 .

출처 https://developer.android.com/reference/androidx/recyclerview/widget/RecyclerView.Adapter

 

 

 

DiffUtil을 사용하기 쉽게 도와주는 ListAdapter, AsyncListDiffer가 있습니다. (백그라운드 스레드에 DiffUtil의 사용을 단순화 함)

AsyncListDiffer는 기존 리스트와 새리스트를 비교 시 아이템이 개수가 많을 경우 비교 연산 시간이 길어질 수 있기 때문에 작업을 비동기로 수행합니다. 따라서 AsyncListDiffer는 자체적으로 멀티스레드가 되어있기 때문에 개발자가 따로 비동기 처리를 할 필요가 없습니다.

 

DiffUtil 사용법

DiffUtil을 사용하기 위해서 DiffUtil.Callback을 구현해 줍니다.

다음으로 DiffUtil.ItemCallback을 구현합니다.

구현한 DiffUtil.ItemCallback은 Adapter 내부에 AsyncListDiffer 객체를 선언해서 사용하면 됩니다.

  private val diffUtil = AsyncListDiffer(this, DiffUtilItemCallback())

 

다음으로는 BaseAdapter에 ListAdapter를 상속시켜 더더 간편하게 사용할 수 있습니다.

 외부에서 아이템 리스트를 교체하는 replaceTo(), 특정 포지션의 아이템을 반환하는 getItem() 등 RecyclerView.Adapter 인터페이스에 맞춰 개발자가 손수 구현해야하는 기능들을 할 필요가 없게 해주는게 바로 ListAdapter입니다.

ListAdapter는 AsyncListDiffer의 wrapper 클래스이기도 합니다.

 

(*AsyncListDiffer에서 currentList는 READ ONLY라서 리스트 변경이 불가하다. 변경을 원할 경우 submitList()를 이용한다. read only 때문에 리스트를 더 신경 써서 관리해야 한다.

submitList란 새 list를 제출할 때 사용합니다.

 

참고:https://zion830.tistory.com/86

 

DiffUtil.Callback 메서드

areContentsTheSame(oldItemPosition: Int, newItemPosition: Int)
두 항목에 동일한 데이터가 있는지 확인하려는 경우 DiffUtil에 의해 호출됩니다.
areItemsTheSame(oldItemPosition: Int, newItemPosition: Int)
두 개체가 동일한 항목을 나타내는지 여부를 결정하기 위해 DiffUtil에 의해 호출됩니다.
getChangePayload(oldItemPosition: Int, newItemPosition: Int)
때 areItemsTheSame(int, int)수익률 true이 개 항목에 대한 areContentsTheSame(int, int)그들에 대해 false를 반환은 DiffUtil는 변화에 대한 페이로드를 얻기 위해이 메소드를 호출합니다.
getNewListSize()
새 목록의 크기를 반환합니다.
getOldListSize()
이전 목록의 크기를 반환합니다.

 

 

DiffUtil.ItemCallback 메서드

areContentsTheSame(T oldItem, T newItem)두 항목에 동일한 데이터가 있는지 확인하기 위해 호출됩니다.
areItemsTheSame(T oldItem, T newItem)두 객체가 동일한 항목을 나타내는지 확인하기 위해 호출됩니다.
getChangePayload(T oldItem, T newItem)때 areItemsTheSame(T, T)수익률 true이 개 항목에 대한 areContentsTheSame(T, T)그들에 대해 false를 반환,이 방법은 변경에 대한 페이로드를 얻기 위해 호출된다.

 

DiffUtil.Callback과 DiffUtil.ItemCallback의 차이

DiffUtil.Callback: 인덱싱 및 항목 비교의 두 가지 역할을 수행

DiffUtil.ItemCallback: 항목 비교만 실행

 

Q. 꼭 DiffUtil.Callback과 DiffUtil.ItemCallback 같이 구현해야 하는 건지

DiffUtil.ItemCallback만 구현해서 써도 되는 건지?

 

 

 

 

 

 

DiffUtil.ItemCallback은 목록에서 null이 아닌 두 항목 간의 차이를 계산하기 위한 콜백입니다.

DiffUtil.Callback목록 인덱싱 및 항목 비교의 두 가지 역할을 수행합니다. ItemCallback은 이들 중 두 번째만 처리하므로 프리젠테이션 계층 및 콘텐츠별 diffing 코드에서 배열 또는 목록으로 색인을 생성하는 코드를 분리할 수 있습니다.

public Object getChangePayload(@NonNull T oldItem, @NonNull T newItem) {
return null;
}

areItemsTheSame(int, int)이 두 항목에 대해 true를 반환하고 areContentsTheSame(int, int)이 두 항목에 대해 false를 반환하면 DiffUtil은 이 메서드를 호출하여 변경 사항에 대한 페이로드를 가져옵니다. 예를 들어 RecyclerView와 함께 DiffUtil을 사용하는 경우 항목에서 변경된 특정 필드를 반환할 수 있으며 ItemAnimator는 해당 정보를 사용하여 올바른 애니메이션을 실행할 수 있습니다. 기본 구현은 null을 반환합니다. 매개변수: oldItemPosition – 이전 목록에서 항목의 위치 newItemPosition – 새 목록에서 항목의 위치 보고: 두 항목 간의 변경 사항을 나타내는 페이로드 개체입니다.

 

목록이 많으면 작업에 상당한 시간이 걸릴 수 있으므로 백그라운드 스레드에서 실행하고 DiffUtil.DiffResult를 가져와서 메인스레드(UI스레드)의 RecyclerView에 적용세요. 또한 구현 제약으로 목록의 최대 크기는 2²⁶개로 제한되어 있습니다.

 

반응형