DiffUtil 이란?
안드로이드의 RecyclerView에서 데이터 변경 시, oldList와 newList의 차이를 계산하여 이를 기반으로 변경된 부분만 업데이트를 진행시켜 업데이트의 효율성을 증가시키는 유틸리티 클래스입니다.
https://developer.android.com/reference/androidx/recyclerview/widget/DiffUtil
DiffUtil 적용 전과 후 갱신 속도 비교
아래는 공식 문서에 기재된 DiffUtil을 적용하기 전과 후의 갱신 속도 비교표입니다.
DiffUtil을 사용했을 경우, 평균 실행 시간과 중앙값 실행 시간 모두 감축되는 것을 확인하실 수 있습니다.
DiffUtil 활용 절차
아래와 같은 data Class가 존재한다고 가정하겠습니다.
data class MyItem(
val id: Int,
val name: String
)
DiffUtil CallBack Class를 생성합니다.
class MyDiffCallback(
private val oldList: List<MyItem>,
private val newList: List<MyItem>
) : DiffUtil.Callback() {
override fun getOldListSize(): Int {
return oldList.size
}
override fun getNewListSize(): Int {
return newList.size
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition].id == newList[newItemPosition].id
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
}
oldList와 newList에 대한 비교가 이루어집니다.
개발자가 조건 명시를 통해 어떤 기준으로 비교하는 아이템이 같은 것인지 다른 것인지를 판별하도록 할 수 있습니다.
이제 DiffUtil을 활용하여 Adapter 클래스에 아이템 갱신에 대한 함수를 구현해 줍니다.
fun updateItems(newItems: List<MyItem>) {
val diffResult = DiffUtil.calculateDiff(MyDiffCallback(itemList, newItems))
itemList = newItems
diffResult.dispatchUpdatesTo(this)
}
fun deleteItem(position: Int) {
val newList = itemList.toMutableList()
newList.removeAt(position)
val diffResult = DiffUtil.calculateDiff(MyDiffCallback(itemList, newList))
itemList = newList
diffResult.dispatchUpdatesTo(this)
}
단, 갱신 대상의 리스트의 사이즈가 너무 클 경우에는 Coroutine을 활용하여 백그라운드 처리해 주는 것이 좋습니다.
CoroutineScope(Dispatchers.Default).launch {
val diffResult = DiffUtil.calculateDiff(MyDiffCallback(itemList, newItems))
itemList = newItems
withContext(Dispatchers.Main) {
diffResult.dispatchUpdatesTo(this)
}
}
갱신이 완료된 이후 Adapter 자체를 인자로 받는 dispatchUpdatesTo()에서는 로직에 따라 아래의 함수 로직이 처리됩니다.
- adapter.notifyItemRangeInserted(position, count)
- adapter.notifyItemRangeRemoved(position, count)
- adapter.notifyItemMoved(fromPosition, toPosition)
- adapter.notifyItemRangeChanged(position, count, payload)
굳이 개발자가 상황에 따라 Notify 함수를 선택하여 호출해 줄 필요가 없게 되는 것입니다.
갱신 시간 적으로도, 개발자 입장에서도 장점이 많은 유틸리티 클래스입니다.