반응형
Notice
Recent Posts
Recent Comments
Link
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Archives
Today
Total
관리 메뉴

코딩하기 좋은날

안드로이드 ListAdapter란?(DiffUtil, AsyncListDiffer) 본문

Android

안드로이드 ListAdapter란?(DiffUtil, AsyncListDiffer)

huiung 2021. 1. 29. 07:24
반응형

RecyclerView

이글을 읽으시는 분들은 아마도, RecyclerView를 사용해 본적이 있으신 분들일 것입니다.

RecyclerView는 다수의 아이템들을 정의한 ViewHolder에 맞게 추가하고, 현재 스크린에 보여지는 아이템들만 생성하여 보여주며, 스크롤 하여 아이템들이 변경될 때 미리 생성된 ViewHolder들을 Recycle하여(ViewCache, RecycledPool 을 활용하여) 효율적으로 사용할 수 있는 컨테이너라고 할 수 있습니다.

 

기본적으로 RecyclerView를 사용하기 위해서는  Adapter, LayoutManager, ViewHolder가 필요한데

이중에서 Adapter는 데이터 리스트를 실제 눈으로 볼 수 있게 item으로 변환하는 중간다리 역할을 합니다.

 

  1. RecyclerView에 보여줄 데이터 리스트 관리
  2. View 객체를 재사용하기 위한 ViewHolder 객체 생성
  3. 데이터 리스트에서 position에 해당하는 데이터를 itemView에 표시

위와 같은 과정을 adapter는 수행하게 되는데 문제는 3번과정을 수행하는 것이 효율적이지 못하다는 것입니다.

일반적으로 데이터의 변경이 생겼을 때 notifyDataSetChagned()를 이용하여 데이터 항목 전체를 업데이트 하게 됩니다.

 

그러나 만약, 무수한 데이터중 소수의 데이터만 변경이 생겼을시 당연히 이러한 요청은 효율적이지 못하게 되며, 해당 아이템만 애니메이션을 적용 하고 싶다거나 하는 상황에서도 문제가 생기게 됩니다.

 

기본적으로 RecyclerView는 아이템의 변경시, 각종 애니메이션을 지원해주는데 아래의 깃에 잘 정리가 되 있는것 같아 참고하면 좋을 것 같습니다.

 

 

johnnymartinovic/RecyclerViewAnimations

Accompanying project for a blog explaining how to create animations in RecyclerView library - johnnymartinovic/RecyclerViewAnimations

github.com

 

자 그렇다면, 전체를 업데이트 하지 않고 단일 아이템을 업데이트 하면 해결이 될 문제입니다. 실제로 adapter에서는 아래와 같이 상황에 맞는 메서드들을 제공합니다.

fun notifyItemInserted(position: Int)
fun notifyItemMoved(fromPosition: Int, toPosition: Int)
fun notifyItemRangeInserted(startPosition: Int, itemCount: Int)
fun notifyItemRemoved(position: Int)
fun notifyItemRangeRemoved(startPosition: Int, itemCount: Int)
fun notifyItemChanged(position: Int)
fun notifyItemRangeChanged(startPosition: Int, itemCount: Int)

 

그러나 위의 메서드들을 보면 position값이 당연히 필요합니다. 변경된 데이터의 position을 알고 있다면 전혀 문제가 없지만 변경된 데이터의 position을 알지 못한다면? 이를 직접 계산할 필요가 있습니다.

 

따라서 이런 문제를 해결하기 위해 가장 초기에 고안된 것이 DiffUtil 클래스 입니다.

 

기존 존재하던 데이터리스트와 교체될 데이터 리스트를 비교하여 실질적으로 업데이트 되어야 할 아이템을 추려 주는 역할을 수행 합니다.

 

DiffUtil

'비교'를 하기 위해서는 DiffUtil.Callback()을 구현해야 합니다.

Callback은 아래의 두 메서드를 구현해야합니다.

  • areContentsTheSame() : 두 아이템이 동일한 내용물을 가지고 있는지 체크한다. 이 메서드는 areItemsTheSame()가 true일 때만 호출된다.
  • areItemsTheSame() : 두 아이템이 동일한 아이템인지 체크한다. 예를들어 item이 자신만의 고유한 id 같은걸 가지고 있다면 그걸 기준으로 삼으면 된다.

DiffUtil.calculateDiff(diffUtil)로 업데이트가 필요한 리스트를 찾습는다.

notifyDataSetChanged()대신 dispatchUpdatesTo(Adapter adapter)를 사용하면 교체가 필요한 아이템에 대해서 부분적으로 데이터를 교체하라는 notify가 실행됩니다.

 

AsyncListDiffer

 

위의 DiffUtil을 이용하여 비교연산을 할 때, 비교연산의 코스트가 크다면 백그라운드 스레드에서 처리 하는 것이 좋을 것입니다. 이를 지원해주며, 편리하게 처리 해주기 위해 나온것이 AsyncListDiffer입니다.

AsyncListDiffer의 submitlist 메서드를 통해 이 모든과정이 처리됩니다.

 

ListAdapter

자 드디어 ListAdapter까지 왔습니다. ListAdapter는 내부적으로 AsyncListDiffer를 사용하면서, RecyclerView의 adapter처럼 이용이 가능합니다. 따라서 우리는 최종적으로 ListAdapter를 상속하는 adapter 클래스를 만들고, ListAdapter의 파라미터에 diffutil의 callback을 구현해서 넘겨주면 내부에서 submitlist( 바뀔 데이터 ) 라는 하나의 메서드로 모든 작업을 처리 할 수 있습니다!!!!

 

 

실제로 어댑터 코드도 간결해지고, submitlist 만으로 모든 처리가 가능해지는 ListAdapter 리싸이클러뷰 구현시 적극 활용 하시면 될 것 같습니다. !!!

 

반응형