[안드로이드/코틀린] DiffUtil 구현시 === 사용에 대한 고찰

Eunho Bae·2022년 1월 24일
0
post-thumbnail

서론

이전에는 RecyclerView의 뷰홀더 갱신이 필요하면 notifyDataSetChanged를 호출해서 전체를 갱신하곤 했다. 하지만 불필요하게 안바꿔도 되는 아이템을 다시 해줘야하고, 무엇보다도 깜빡임이 심해지는 경험을 하였다.

하지만 DiffUtil이라는 클래스의 내부 추상 클래스인 ItemCallback을 가져온 후 내부 추상메서드를 오버라이드해서 구현을 함으로써 최적화를 이루어 낼 수 있었다.

DiffUtil

areItemsTheSame와 areContentsTheSame은 뷰홀더 갱신 유무를 정해주는데
areItemsTheSame에서
만약
ViewHolder1 { a = 1 }
ViewHolder2 { a = 2 }
ViewHolder3 { a = 3 }
이렇게 화면에 출력이 되었고,
ViewHolder1의 모델에서 a 변수가 11로 변경되어 화면에 출력을 해야한다고 했을때, 뷰홀더 3개를 전부 갱신하는게 아니라 ViewHolder1만 변경시켜 줄 수 있다.

areItemsTheSame(ViewHolder1 { a = 1}, ViewHolder1 { a = 11 })
이렇게 oldItem, newItem 매개변수로 각각 들어왔다고 가정하고, 뷰홀더에서 얹어놓은 모델에 내가 직접 정의해놓은 변수 id, type는 변하지 않았다고 하자. (id는 뷰홀더 사이에서 고유한 값을 지님)
또 return oldItem.id == newItem.id && oldItem.type == newItem.type 으로 정의를 했다고 하자. 간단히 먼저 가장 기본적으로 동일한 아이템(뷰홀더)인지 판단하기 위해 id와 type을 비교한다고 정의한 것이다.

그렇다면 areItemsTheSame 매서드 내부에서 id와 type이 같으니까, 리사이클러뷰 첫번쨰 뷰홀더는 화면에서 갱신을 안해도 되나??라고 생각할 것이다.
areContentsTheSame은 areItemsTheSame이 true (즉, id와 type이 같은 경우)를 반환할때 한번더 내용물(content)을 검사하기 위해 정의한 함수인데, areContentsTheSame을 호출해서 다시한번 더 검사를 진행을 하도록 제공되는 함수 같았다.

이 메서드에서도 매개변수에 동일하게 들어오는데, 어떤 코드에서는
return oldItem === newItem으로 매서드를 정의하고 있었다. 왜 굳이 ===을 쓰는지 궁금하여 브레이크 포인트를 걸어서 확인을 해봤는데 애초에 areContentsTheSame 메서드가 호출이 되는 경우를 만들기 어려웠다.

스택 오버플로우의 어떤 게시글에 의하면 코틀린에서 ===은 내용물이 같은지 뿐만 아니라 두 모델이 메모리상에서 같은 인스턴스인지 비교를 해주는 것이라고 한다.
즉, 자바로 치면 equals()와 ==을 동시에 호출한 셈이 되는 것인데, mocking 데이터 리스트를 두개 만들고 첫번째 뷰홀더만 내용(a)을 바꿔주는 버튼을 만들어서 areContentsTheSame을 호출하도록 유도했다.

areContentsTheSame(oldItem, newItem) { return oldItem === newItem }

그러면 oldItem과 newItem의 내용 전체를 비교하고 메모리 상에서 같은 인스턴스인지를 비교할 것인데, ===로 정의할 경우 뷰홀더 전체가 갱신되는 현상을 볼 수 있었다.
즉, 내용물의 변화가 없는 뷰홀더도 oldItem과 newItem이 메모리 상에서 다르기 때문에 쓸데없이 갱신이 되었다는 것이다(추측). 그래서 모델 클래스 내에 정의한 변수만 체크를 하는 ==을 사용했더니 true를 리턴했다. 즉 변화가 생긴 뷰홀더만 갱신이 되는 것을 볼 수 있었다.

profile
개인 공부 정리

0개의 댓글