RecyclerView: notifyDataSetChanged 쓰지 말자!

Murjune·2024년 5월 19일
2

Android

목록 보기
1/1
post-thumbnail

리사이클러뷰는 2년 전에 SOPT 라는 동아리에서부터 접해보았고, DiffUtil 과 ListAdapter 에 대해 알고 난 이후로부터는 notifyXXX 함수들을 등한시하게 되었다.

사실 ListAdapter 도 얼마 쓰지 않았다.
바로, 컴포즈로 넘어갔기 때문 😂

하지만, 우테코에서는 컴포즈가 아닌 기존 Android View 시스템을 사용하고 있고, RecyclerView 는 View 시스템을 사용한다면 뗄레야 뗄 수 없는 친구이기에 잘 알고 있어야 한다.

그리고, 저번 수업시간에 우테코 코치 레아는 만약 자신이 면접관이라면 리사이클러뷰에 대해서는 물어볼 것이다..! 라고 하셨기 때문에 더욱 공부하고 정리해야겠다는 마음을 먹었다 💪

RecyclerView 에 대해 딥하게 공부를 하다보니 생각보다 이 친구 쉽지 않는 녀석이였다. (아니 진짜 개어려운데..?)

그래서, 우테코 레벨 2가 끝나기 전에 리사이클러뷰와 View 시스템(리사이클러뷰를 잘 알려면 View 에 대해 잘 알아야한다)에 대해 딥다이브 하고 정리해볼 계획이다.

아.. 블로그 글 쓰다가 중단된 거 많은데 언제 다쓰지..?

그리고, 이번주 블로그글을 뭘 쓸지 고민하다가 이번 쇼핑 카트 미션에서 ListAdapterDiffUtil 을 금지 당하기도 했고, 리사이클러뷰를 최적화하는 가장 쉬운 방법이 notifiyXXX 함수들 적절히 사용하는 것이기에 정리하고자 한다.

notifyDataSetChanged

   fun updateProduct(newProducts: List<CartProductUi>) {
        products = newProducts
	    notifyDataSetChanged()
    }

보통 RecyclerView나 ListView 를 사용해본 사람이라면
notifyDataSetChanged() 를 통해 뷰를 업데이트했을 것이다.

사용하기도 매우 쉽고, 네이밍도 직관적이다.
Adapter야 나 데이터 바뀜, 이대로 반영해줘!

그러나, notifyDataSetChanged() 함수를 사용하면 비효율적이기 때문에 안스에서 아래와 같이 린트를 뿜는다.

왜 그럴까??

notifyDataSetChanged() 를 사용하게 되면 모든 아이템 View 들이 다시 bind 및 layout 된다. (다시 그려지고, 부모 뷰 그룹에 다시 붙는다는 말이다.)

만약, 100개의 List 가 있다고 가정을 해보자.
난 2번 item과 20 번 item 을 Swiping 하고 싶다.

그렇다면, 2번과 20번 item 만 다시 그리는 것이 제일 효율적일 것이다.

그러나, notifyDataSetChanged() 함수는 100개의 item을 다 다시 그리고 재배치시킨다.

notifyDataSetChanged() 함수는 정말 최후의 순간에 사용해야할 것이다.

레아는 notifyDataSetChanged() 를 써야할 때가 있다고 한다. 하지만, 그게 언제인지 아직 잘 모르겠다 😅 알게 되면 추가로 포스팅하겠다..!


효율적으로 데이터 변경하는 방법

리사이클러뷰 데이터 변경 이벤트에는 다음과 같이 크게 2가지로 분류된다.

1) item 변경: 단일 item의 데이터만 업데이트하는 경우
2) 데이터 구조 변경: 단일 item 을 삽입, 제거 또는 이동하여 데이터의 위치나 구조가 변경될 경우

데이터 변경 이벤트에 따라 최적화하도록 RecyclerView.Adapter 에서는 다양한 notifyXXX() 함수들을 제공한다.

1) item 변경

notifyItemChanged(postion: Int)
notifyItemRangeChanged(int positionStart, int itemCount)

notifyItemRangeChanged() 을 활용하여 position 에 해당하는 item 의 Data 만 업데이트하여 다시 그려줄 수 있다!

fun updateItem(position: Int, newItem: Product) {
      items[position] = newItem
      notifyItemChanged(position)
}

만약, 여러 position 에서 data 가 변경됐을 경우에는 notifyItemRangeChanged 를 사용하자!

public final void notifyItemChanged(int position) {
    mObservable.notifyItemRangeChanged(position, 1);
}

참고로, notifyItemChanged은 내부에서notifyItemRangeChanged을 호출하고 있다.

2) 데이터 구조 변경

2 - 1) 삽입(추가)

notifyItemInserted(position)
notifyItemRangeInserted(position, itemCount)

특정 position 에 data를 삽입하거나 추가해주고 싶다면 notifyItemInserted(position) 를 활용해주면 된다.

fun addItem(item: Product) {
    items.add(item)
    notifyItemInserted(items.size - 1)
}

만약, 여러 데이터를 추가해주고 싶다면 notifyItemRangeInserted 를 사용하자!

해상 영상은 notifyItemRangeInserted 을 활용한 것이다 😸

2 - 2) 삭제

notifyItemRemoved(position) notifyItemRangeRemoved(position, itemCount)

특정 position 에 data를 삭제하고싶다면 notifyItemRemoved(position) 를 활용해주자

2 - 3) 이동(drag&drop)

notifyItemMoved(fromPosition, toPosition)

해당 notifyItemMoved 는 drag & drop 과 같은 특수한 상황에서 사용하는 함수이다.

1 번 위치를 4번 위치와 drag & drop 으로 swipe 하고 싶다면 해당 함수와 ItemTouchHelper를 함께 사용하면 된다.

 fun moveItem(fromPosition: Int, toPosition: Int) {
        if (fromPosition < toPosition) {
            for (i in fromPosition until toPosition) {
                Collections.swap(items, i, i + 1)
            }
        } else {
            for (i in fromPosition downTo toPosition + 1) {
                Collections.swap(items, i, i - 1)
            }
        }
        notifyItemMoved(fromPosition, toPosition)
    }

필자는 과거에 프로젝트에서 item의 순서를 수동으로 바꿔야하는 경우가 있어 사용해본 적이 있는데, 요즘은 해당 UX/UI 가 사용되는 앱을 찾아보기 힘들다 😅

그런데.. 너무 귀찮은데요 😵

지금까지, RecyclerView의 data set을 효율적으로 변경하는 notifyxxx 함수들에 대해 배웠다.

그러나, 개발자가 Adapter data 를 매번 상황에 맞게 해당 적절한 함수를 사용하고, data를 조작하는 일은 굉장히 피곤하다..

따라서, 필자는 DiffUtil & ListAdapter 를 사용할 것을 적극 권장하지만 그래도 해당 함수들을 한 번씩 사용해보고 리팩토링하는 경험을 해보는 것을 추천한다!

notifyxxx 함수들을 사용해보면서 어떠한 이유로 DiffUtill 이라는 개념이 나왔는지 체감하게 된다면 RecyclerView 와 DiffUtil 에 대한 이해도가 늘어 날 것이기 때문인다.

필자는 우테코 쇼핑 장바구니 미션에서 DiffUtil 을 참고해서 ItemUpdateHelper 라는 Util class 를 만들었다.

심심하신 분들은 해당 file 봐주세요.. 🙇‍♂️

Reference

https://stackoverflow.com/questions/33789345/whats-better-notifydatasetchanged-or-notifyitemchanged-in-loop

https://medium.com/@wodbs135/recyclerview%EB%A5%BC-%EB%8D%94-%EC%9E%98-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95-879cd52bcdba

https://todaycode.tistory.com/55

https://gift123.tistory.com/67

profile
열심히 하겠슴니다:D

0개의 댓글