본문 바로가기

안드로이드

[안드로이드] ListAdapter의 작동 원리 및 갱신이 안되는 경우

개요

RecyclerView를 활용하여 목록을 리스팅할 때 흔히 사용하는 어답터로 RecyclerView.Adapter와 ListAdapter로 나뉜다. 전자는 아이템 목록을 직접 관리하며 값이 변경될 경우 변경된 범위, 항목에 대해 직업 notify를 해주어야 한다. 하지만, ListAdapter는 이런 작업이 필요가 없는 어답터이다. ListAdapter는 어떻게 동작할까?

 

ListAdapter의 개념

ListAdapter는 RecyclerView.Adapte를 베이스로 하는 클래스로 아이템의 차이(diff)를 백그라운드 스레드에서 처리해준다. 

 

ListAdapter의 작동 원리

우선 사용 방법은 매우 간단하다. ListAdapter의 submit 함수로 리스트를 전달해주면 된다. 그 뒤에서는 AsyncListDiffer가 동작하여 리스트의 차이를 계산하고 변화를 처리해준다. 이를 위해 DiffUtil.ItemCallback을 구현해야 한다. 여기서는 아이템이 같은지 확인하고 만약 같을 경우 아이템의 세부 내용(contents)도 같은지 확인하여 다른 경우 이 아이템은 갱신의 대상이 된다.

companion object {
        val diffUtil = object : DiffUtil.ItemCallback<Object>() {
            override fun areContentsTheSame(oldItem: Object, newItem: Object) =
                oldItem == newItem

            override fun areItemsTheSame(oldItem: Object, newItem: Object) =
                oldItem.id == newItem.id
        }
    }
}

위에서 중요한 점은 areItemsTheSame에서 아이템이 같음을 판단할 때 id 값과 같은 고유한 값을 이용해야한다.

 

 

ListAdapter를 사용할 시 마주칠 수 있는 이슈

하지만, 이런 ListAdapter로 제대로 목록을 갱신시키지 못하는 경우도 존재한다. 기본적으로 해당 라이브러리는 Room이나 API를 통해 새로운 비동기 리스트를 매번 갱신받아 갱신하는 경우를 가정한다. 즉 리스트의 주소(참조)가 계속 바뀐다는 것을 가정한다.

 

아래는 실제 submit 리스트를 통해 수행되는 함수 내부이다. 실제로 참조가 같은 경우 더 이상 동작하지 않게 구현되어 있다.

ListAdapter의 submitList
AsyncListDiffer의 submitList 내부에서 newList==mList인 경우 별 동작 없이 return 된다.

 

실제 차이를 계산하는 구현부를 확인하면 list의 주소가 같지 않은 경우에만 실제 아이템 비교로 넘어갈 수 있다. 그러므로 매번 Room이나 API를 통해 데이터를 가져오는 작업이 아니라 앱 내에서 참조가 변경되지 않는 하나의 리스트를 유지하면서 값이 변경될 경우에도 반영하기 위해서는 submitList 함수 호출 시 주소가 다른 리스트를 넘겨주어야 한다.

 

코틀린에서는 다음과 같이 간단하게 이를 구현할 수 있다.

.observe(this, Observer {
    adapter.submitList(it?.toMutableList())
})

리스트 콜렉션에 toMutableList를 활용할 경우 새로운 내부 목록은 똑걑은 새로운 참조 리스트가 반환된다.

 

 

참고