Development/Android

[Android] DiffUtil 활용하여 RecyclerView 성능 향상

SeungYong.Lee 2024. 1. 24. 16:04
반응형

DiffUtil 이란?

안드로이드의 RecyclerView에서 데이터 변경 시, oldList와 newList의 차이를 계산하여 이를 기반으로 변경된 부분만 업데이트를 진행시켜 업데이트의 효율성을 증가시키는 유틸리티 클래스입니다.

https://developer.android.com/reference/androidx/recyclerview/widget/DiffUtil

 

DiffUtil  |  Android Developers

androidx.appsearch.builtintypes.properties

developer.android.com

 

DiffUtil 적용 전과 후 갱신 속도 비교

아래는 공식 문서에 기재된 DiffUtil을 적용하기 전과 후의 갱신 속도 비교표입니다.

By Chat GPT

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 함수를 선택하여 호출해 줄 필요가 없게 되는 것입니다.

갱신 시간 적으로도, 개발자 입장에서도 장점이 많은 유틸리티 클래스입니다.

반응형