개요
- 이전에
RecyclerView에 대한 글을 정리한 적이 있었다. 당시에는, RecyclerView의 등장 의의와 RecyclerView를 사용하는 경우에 구현해야 하는 구현부에 대한 내용을 중심으로 작성했었다. - 2022.12.22 - [IT/Android] - [Android] RecyclerView 이해하기
- 이외에
RecyclerView에서 사용하는 Adapter를 중심으로 알아봤었다 - 2022.12.23 - [IT/Android] - [Android] RecyclerView 갱신의 효율성을 높이는 방법
- 그러나
RecyclerView를 사용하면서, 근본적인RecyclerView에 대한 이해를 필요로 하는 일이 늘어났고 특히,RecyclerView의 내부 동작에 대한 이해가 필요로 하는 일이 늘어남에 따라 이번에는 RecyclerView의 내부 구조 동작을 중심으로 알아보고자 한다.
목적
RecyclerView의 작동 방식 및 LifeCycle을 중심으로 알아볼 예정이다.- 이외에 ListView의 문제점을 비롯한 RecyclerView의 등장 배경과 정의, 성능 향상방법 등을 Google I/0 2016에 소개된 내용을 중심으로 알아볼 예정이다.
RecyclerView의 등장 의의
등장 이전: ListView
- 안드로이드에선
List를 보여주기 위해ListView를 사용했었다. 그러나,ListView에는 고질적인 문제가 존재했다ListView의ItemView생성속도ItemView애니메이션 구현의 어려움ItemView재사용 여부 분기 처리- 변경된
Item Data의Position을 알 수 없음
ListView 문제점 - 1. ListView의 ItemView 생성속도
- 대표적인 문제점으로, 아이템 리스트를 표현하는
ItemView의 개수가 수백개 이상으로 많아지는 경우,ListView가ItemView를 생성하는 속도가 매우 느려진다는 것이었다.- 이에 따라 구글은 내부적으로
Adapter라는 것을 사용해, 당장 눈에 보이는ItemView만 생성하는 대응책을 내놓았고, 눈에 안보이던 새로운ItemView가 눈에 보여져야 할 때 기존에 생성했던ItemView를 그대로 재사용한다는속임수를 사용했었다. (ViewType이 동일한 경우에만) - →
ListView가ItemView를 재사용하지 않고 한번에 그리기 때문에 속도가 느리다 는 말은ListView의Adapter라는 재사용 메커니즘을 사용하지 않았을 때 적용되는 말이다.
→ 즉,ListView도View를 재활용 할 수 있다.
문제는, 이 재사용에 의해 후술할 애니메이션 처리라는 문제점이 생겨났다. - 이러한 문제들을 비롯해
ListView에 대한 개발자들의 요구사항이 점점 커지고,ListView API가 지나치게 복잡해짐에 따라 오작동을 일으키는 경우가 빈번해지기 시작했다
- 이에 따라 구글은 내부적으로
ListView 문제점 - 2. ItemView 애니메이션 구현의 어려움
- 또한
ListView가 가진 가장 큰 문제는,ItemView에 애니메이션을 구현하는 것이 너무 어렵다는 것ItemView를 제거하는 애니메이션을 구현한 경우,ItemView를 하나씩 클릭하여 제거했을 경우에는 원하는 대로 작동하는 것 같았지만 여러 개의ItemView를 클릭한 후 빠르게 스크롤하면 몇몇ItemView가 아무런 데이터도 없는 상태로 비어있는 현상이 발생하게 된다.
(ItemView의 애니메이션이 진행되는 동안 재사용 대상으로 선택 됐기때문이다)

-
- 애니메이션 효과가 완전히 끝나면 이
ItemView에 해당하는 객체가 메모리 상에서 제거되지만, 이 객체는 재사용되기로 약속된 객체이기 때문에 다시 사용되어야 한다. - 하지만 애니메이션으로 인해 메모리 상에서 제거된 경우,
ListView가 새로운 데이터를 세팅할 재사용ItemView를 메모리에서 찾지 못하게 되는데 이를 `ListView의 붕괴 현상`이라고 부른다. - → 현재는
ListView의 붕괴 현상을 해결하기 위해 안드로이드가 제공하는ViewPropertyAnimatorAPI를 사용하면 된다. 이것을 사용하면 애니메이션 효과가 진행 중인ItemView는 작업이 진행중인 객체로 인식되어 재사용 대상으로 선택되지 않게 된다.
- 애니메이션 효과가 완전히 끝나면 이
ListView 문제점 - 3. ItemView 재사용 여부 분기 처리
- 이외에
ListView를 구현할 때는 개발자가 직접ItemView를 생성하는 로직(onCreate)과 데이터를 연결하는 로직(onBind)을 분리해서 코드를 작성해야 했다.- 따라서 개발자들은 if 조건문을 통해 재사용될
ItemView가 존재하면 바로 데이터를 재사용될ItemView에 연결하고,
재사용될ItemView가 존재하지 않으면(null) 새로운ItemView를 생성하는 코드를 추가해야 했다.
- 따라서 개발자들은 if 조건문을 통해 재사용될
- 문제는 이 로직 분리 작업을 까먹는 경우도 많았고 이로 인해
ListView의ItemView재사용 기능이 동작하지 않게 돼 앱의 성능을 악화시키는 원인이 되기도 했다.
ListView 문제점 - 4. 변경된 Item Data의 Position을 알 수 없음
ListView에 보여질 데이터가 바뀐 경우, 기존의 ListView의 Adapter는 데이터가 변경되었다는 사실만 알 수 있을 뿐 구체적으로 목록에서 몇 번째 데이터가 변경되었는지 인식할 수 없었다.- 예를 들어, 사용자의 눈에 보이고 있는
ItemView중3번째 ItemView에 연결된 데이터가 어떠한 처리에 의해 바뀌게 되면Adapter는 데이터가 바뀌었다는 사실만 알 뿐몇 번째 ItemView에 연결된 데이터를 바꿔야 하는지는 알 수 없었다. - 따라서
3번째 ItemView의 데이터를 바꾸기 위해 모든 ItemView에 데이터를 다시 연결해야 했다 - → 이러한 문제는 애니메이션 처리를 더욱 어렵게 만드는 원인이 되기도 했다.
- 예를 들어, 사용자의 눈에 보이고 있는
- 위와 같은 문제들이 발생하자 Google Android API 개발팀은 Google I/O 2016에서 기존
ListView설계에 실수가 있었음을 인지하고 이 실수를 반복하지 않고자RecyclerView를 개발했다고 발표하였다
ListView는 한번에ItemView를 그리기 어려웠음
→ 속임수 1) 눈에 보이는 ItemView만 생성
→ 속임수 2) 맨위에 있던 ItemView를 그려져야할 위치에 재사용
→ 문제 1) 개발자들 요구사항 증가
→ 문제 2) 다른 뷰에서 제공하는 비슷한 기능이 ListView에 추가되며 혼란 → 문제3)애니메이션 처리 문제
→ 문제 3) 재사용될 ItemView인지 분기처리 필요
→ 문제 4) Adapter는 변경된 Item Data의 Position 알 수 없음
⇒ RecyclerView 등장
RecyclerView의 정의
RecyclerView는 안드로이드 프레임워크에서 제공하는 View 레이아웃 중 하나이다.RecyclerView는 사용자가 관리하는 많은 수의 데이터 집합(Data Set)을 개별 아이템 단위로 구성해 화면에 출력하는뷰그룹(ViewGroup)이며, 한 화면에 표시되기 힘든 많은 수의 데이터를 스크롤 가능한 리스트로 표시해주는위젯이다RecycleView는 Android Jetpack 구성 요소 중 하나이기 때문에안드로이드 프레임워크에서 라이브러리 형식으로 제공하고 있다.
→ 개발자는 이 라이브러리를 가져다 사용해서 원하는 모양대로 응용하기만 하면 되는 것이다
RecyclerView의 성능 향상 방법
ViewHolder 패턴을 통해ListView의 성능을RecyclerView에서 크게 향상할 수 있었다. 재활용하는View들의 클래스를View 태그또는Array에 저장하고, 필요할 때 바로 가져와서 사용하는 방법으로 성능을 크게 향상하였다.ListView에서 재활용되는View를 해당 포지션에 맞게 가져오는 곳에서 성능을 향상할 수 있었다면,RecyclerView는 가져온View에 데이터를Bind하는 경우 최적화할 수 있는 방법을 제공하고 있다.
RecyclerView의 동작 원리
RecyclerView는 내부 아키텍처는 컴포넌트 기반의 아키텍처이며, 내부 아키텍처를 구성하는 컴포넌트는RecyclerView,LayoutManager,ItemAnimator,Adapter등이며, 이 중 아래의 3가지 컴포넌트가 가장 중요하다- 컴포넌트 기반 아키텍처: 이렇게
RecyclerView,LayoutManager,ItemAnimator,Adapter등의컴포넌트가 서로 상호작용하여 하나의 목록형 레이아웃을 동작하게끔 만들기 때문에 컴포넌트 기반 아키텍처
- 컴포넌트 기반 아키텍처: 이렇게

- 이 3가지 컴포넌트가 적절하게 상호작용해야
ItemView를 올바르게RecyclerView안에 배치할 수 있다
1. LayoutManager
LayoutManager 정의
LayaoutManager은RecyclerView가 Item을 화면에 표시할 때,ItemVIew들이RecyclerView내부에서 배치되는 형태를 관리하는 컴포넌트이다LayoutManager는선형,그리드,엇갈린 그리드모양으로RecyclerView의 모습이 보여지도록 하는 작업을 담당하는컴포넌트이다Layoutmanager는 각각LinearLayoutManager(선형),GridlayoutManager(그리드),StaggeredLayoutManager(크기가 각각인 엇갈린 그리드)로 분류된다
- 즉,
RecyclerView는 자신이 어떤 모습으로 그려질지는 모르며, 오직LayoutManager가 담당하며,RecyclerView의 모양을 만드는 작업을 책임지고,RecyclerView모양에 따라ItemView를 적절한 위치에 배치하는 작업을 담당한다.
LayoutManager가 하는일
- (1) 만약 유저가 리스트를 더 보기 위해
RecyclerView를 위로 스크롤 했다고 가정해보는 경우, - (2)
RecyclerView는 새로운ItemView를 보여줘야한다는 것을 인식한다.

- (3) 하지만 새로운
ItemView를 어디에 배치할 지는LayoutMananger가 알고 있다. - (4) 그러므로, 위 상황에서
RecyclerView는LayoutManager에게 새로운 ItemView를 보여달라고 메시지를 전달한다.
(5) 그럼LayoutManager는 적절한 위치에 데이터가 연결된 ItemView를 연결하게 된다.
2. Adapter
RecyclerView에서도ListView와 동일하게Adapter에 의존한다.
(다른 adapter처럼 뷰와 데이터를 Bind)- 하지만
ListView Adapter와의 차이점은ItemView생성 외에도ViewHolder라는 것을 생성하는 작업을 담당한다는 것이다.RecyclerView는ViewHolder를 기본적으로 사용하기 때문에,View를 생성하지 않고,inflated(XML의 레이아웃이 메모리에 객체화된)뷰를 갖는ViewHolder를 생성하게 된다.
→ViewHolder가 생성되어 캐시가 쌓이면 필요할 때 재사용된다- 이 때 기억해야 할 점은
ViewHolder는position이 아닌itemViewType에 의해 생성되는 것이다.
→ 이렇게 함으로써 재사용할 뷰를 더 쉽게 찾고 쉽게 추가할 수 있게된다.
Adapter가 하는 일
View와ViewHolder를 만든다ViewHolder에 아이템을 바인드RecyclerView에게 데이터셋이 변경 되었을 때 이를 알린다- 각각 데이터의 변경 이벤트를 처리
- 아이템의 상호작용(ex: 클릭)을 처리
ItemView의ViewType이 여러 가지일 경우를 처리- 재활용 실패에 대한 수습을 처리 (
onFailedToRecyclerView)
onBindViewHolder()
onBindViewHolder()는parameter로ViewHolder객체와position값을 받고 Return 타입은 없다.position값은Adapter클래스 property 인 데이터셋의 특정 포지션이다.
예를 들어 데이터 셋이 배열 자료구조로 구성되었으면position은 배열의 특정index인 것이다.ViewHolder객체는 데이터 셋의 특정position에 저장되어 있는 아이템을 보여주기 위해 업데이트 되어야하는ViewHolder이다
onBindViewHolder()메소드는특정 position의 데이터(item)을 보여주기 위해RecyclerView가 호출하는 것이다.- 즉 호출하는 주체가
RecyclerView인데RecyclerView는 내부의tryBindViewHolderByDeadLine()이라는 메서드 내부에서mAdapter.bindViewHolder()를 호출한다.bindViewHolder()에서onBindViewHolder()가 호출된다
- 즉
RecyclerView가 특정 상황이 발생했을 때 이를 알리기 위해bindViewHolder를 호출하고, - 이 알림을 받았을때 해야하는 작업을 우리가
Adapter내에onBindViewHolder()메소드를 오버라이딩해 함수 내부에서는RecyclerView.ViewHolder.itemView의 컨텐츠를 업데이트하는 작업이 실행된다.
→ 업데이트만하고 반환되는 작업이 없기에 onBindViewHolder()는 콜백함수라는 걸 알 수 있다
ViewHolder의 LifeCycle
ViewHolder의LifeCycle는 대부분의 개발자들은ViewHolder를 작성하는 것에 시간을 많이 쓰기 때문에, 아는 것이 매우 중요하다
ViewHolder Lifecycle - 1. Birth
1. 요구하는 ItemView가 캐시에 저장되어 있을 경우

- (When) 유저가 스크롤 이벤트를 발생시
RecyclerView가LayoutManager에게 새로운ItemView의 위치를 요구한다. - (1)
LayoutManager는 메시지를 받고 → 어떤 포지션에 새로운ItemView가 배치되어야 하는지를 계산하고
(2) 이 위치를 다시RecyclerView에게 알리는데,
(3) 위치를 보내주고 이 위치에 배치할ItemView를 요청한다.
(4)RecyclerView는 캐시에 해당 포지션에 배치되도록 지정된ItemView가 있는지를 확인한다
(recyclerview.getViewForPosition())
(5) 만약 이 포지션에 배치되어야 하는ItemView가 캐시되어 있을 경우
→ 이ItemView를 받아서 다시LayoutManager에게 전달한다.
(Recyclerview내부 동작 원리 상 일정 양의 ItemView를 캐시에 저장해 놓기 때문)
2. 요구하는 ItemView가 캐시에 없을 경우

- (4) Cache에 저장된
ItemView가 없다는 메시지가 날아오면
(5)RecyclerView는Adapter에게(RecyclerView → Adapter)
해당하는ItemView의ViewType을getViewType메소드를 통해 물어본다 (adapter.getViewType())
(6)Adapter는 해당하는ViewType을 알려주고
(7)RecyclerView는 이ViewType을 가지고Recycled Pool에서 해당하는 ViewHolder가 있는지 물어본다 (getViewHolderByType메소드)- 여기서 사용하는
Recycled Pool은 공유되는 Recycled Pool일 수도 있고,
오직 이RecyclerView에서만 사용하는 유일한Recycled Pool일 수도 있다.
- 여기서 사용하는
3. Recycled Pool에 ViewHolder가 존재하지 않는 경우

- (8)
ViewType에 대한ViewHolder가 없을 경우
(9)RecyclerView는Adapter에 새로운ViewHolder를 생성하라는 지시를 내리고
(createViewHolder)
(10)Adapter는 생성한ViewHolder를RecyclerView에 전달
4. ViewHolder가 존재하는 경우 (이미 생성된 경우)

Recycled Pool에 해당ViewType을 위한 해당하는ViewHolder가 존재하는 경우
(8)Recycled Pool은ViewHolder를RecyclerView에 전달하고
(9)RecyclerView는Adapter에게position과ViewHolder를 넘겨주며bind해달라는 명령을 내린다.
(10)Adapter는 bind 작업이 완료된ItemView를RecyclerView에 전달하고
(11)RecyclerView는 이ItemView를LayoutManager에게 최종적으로 전달한다
5. 마지막 단계

- (12) 마지막으로 LayoutManager는 해당 위치에 ItemView를 배치하고 RecyclerView에게 배치가 완료되었다는 메시지를 보낸다 (addView())
(13) 그러면 RecyclerView는 Adapter에게 해당하는 position에 ItemView가 잘 배치되었다는 메시지를 보낸다 (onViewAttachedToWindow())
⇒ 결국 전체 과정을 살펴보면 Layout Manager가 어떤 position에 배치될 ItemView를 요청할 경우 해당 ItemView가 Cache에 저장되어 있다면 Adapter를 거치지 않고 바로 Layout Manager에게 해당 ItemView를 전달할 수 있음을 알 수 있다.
1) 레이아웃 매니저가 getViewForPosition 으로 view를 요청
2)
RecyclcerView는 캐시에getViewForPosition으로 확인. 있으면LayoutManager에게 반환3) 캐시에 없으면
adapter에게type이 뭔지 물어보고Recycled Pool에getViewHolderByType으로 요청4)
Pool에 있으면 반환, 없으면adapter에게createViewHolder로 아이템 생성5)
View를 찾으면adapter에서bindview를 하고LayoutManager에게 Return.6)
LayoutManager는RecyclerView에게addView를 수행하고adapter의onViewAttachedToWindow가 호출됨.
캐시와Recycled Pool은RecyclerView안에 이미 선언되어있음.
setItemViewCacheSize함수로 캐시 사이즈 변경 가능.
ViewHolder Lifecycle - 2. Recycle (ItemView를 캐시에 저장하는 원리)
ViewHolder의 Birth한 다음의 재활용 과정- 사용자가 스크롤을 위로 올려 기존의
ItemView가 화면에서 사라지는 경우 일어나는 일

- (1)
LayoutManager가 화면에서 벗어난ItemView의 position을 계산하고RecyclerView에게 이를 알린다 (removeAndRecycleView)그러면
(2)RecyclerView는 화면에서ItemView를 제거한 후Adapter에게 이를 알리게 되고, (onViewDetachedFromWindow()) →ItemView안에 있는 것들의 캐싱을 해제할 수 있게 된다. - (3)
RecyclerView는 Cache에게 제거되는 position의ItemView가 캐시에 계속 남아있어도 되는 것인지를 Cache에게 물어본다.- 만약, 이
ItemView가 사용된 지 오래된ItemView라면,
(3–1) Cache는Recycled Pool에게 이 오래된ItemView를 전달하고,
(3–2)Recycled Pool은Adapter에게 이ItemView를 메모리에서 제거해도 된다는 메시지를 보낸다.
- 만약, 이
- 그렇지 않고 계속 캐시에 저장할 필요가 있는 경우,
(4) Cache에 계속 저장하라는 지시를 내리고, 이를 통해 나중에LayoutManager가 해당 position에 대한ItemView를 요청할 경우Adapter를 거치지 않고 사용할 수 있다.
ViewHolder LifeCycle - 3. Fancy Reserves
LayoutManager가 다시 레이아웃을 계산하는 도중adapter가 변경되는 경우이다.- 몇몇 View가 더 이상 사용하지 않게 되었을 경우의 상황은 아래 그림과 같다.

- (1)
RecyclerView가LayoutManager에게 사용되지 않는ItemView가 있음을 알린 후,
(2) 사라지는ItemView들을 다시ViewGroup에 추가한다.
그런 다음, (3)LayoutManager에서 이ItemView들을 숨긴다.
이 때 (4)RecyclerView는 이ItemView들에 대해ItemAnimator에게 애니메이션 처리를 요구하게 되고, 애니메이션 처리가 끝나면 (onAnimationFinished)
(5)Adapter의onViewDetachedFromWindow를 호출해 제거가 완료되었음을 알린다.
그 후 (6)Cache와Recycled Pool에게 캐시를 업데이트하고 제거된ItemView를 재활용하라고 지시한다.
ViewHolder Lifecycle - 4. Death
ViewHolder를 잃는 경우이다- 이 경우 성능 문제가 발생하기 때문에 이는 매우 중요하다
Death Case #1

- 1번째
ViewHolder소멸은ItemAnimator를 사용하지 않고 애니메이션을 구현하는 경우 발생가능하다- (1)
LayoutManager는ItemView가 더 이상 필요하지 않고 이ItemView가transientState에 빠진 경우(애니메이션이 진행 중인 상태, 예를 들어Fading out) 이를RecyclerView에게 알리는데, - (2)
RecyclerView는 이ItemView가 유효한지 확인한 후, 유효하지 않다면 이를Recycled Pool에 알린다. - (3)
Recycled Pool에서 다시 한 번transientState인지 확인하고, 해당 상태라면 재활용에 사용할 수 없으므로Adapter의onFailedToRecycle()을 호출한다. - → 여기서 우리는 임의로 Adapter의 onFailedToRecycle의 리턴값을 true로 재구현해 Recycled Pool에게 “날 믿고 재활용해라” 라는 메시지를 전달 할 수도 있다.
- 하지만 기본적으로
Adapter의onFailedToRecycle()을 호출하면false를 리턴하도록 구현되어 있어서ViewHolder는 파괴된다. - → 이는 우리가 원치 않는 결과이며, ItemAnimator를 사용하지 않고 애니메이션을 구현할 경우 해당 상황에 빠질 수 있다.
- →
ItemAnimator를 사용하면 올바른 Lifecycle 이벤트를 수신하여ItemView를 재활용할 수 있게 도와준다.
- (1)
Death Case #2

- (1) `RecyclerView`에서 `ItemView`에 대한 처리를 마쳤고 이를 `Recycled Pool`에게 알린다.
- Recycled Pool은 제한된 크기를 가지고 있기 때문에 해당하는 `ViewHolder`에 대해 더 이상 남는 자리가 없는 경우 `ViewHolder`가 제거된다.
- 일반적으로 이는 다음과 같은 경우에 발생한다
1. 많은 `ViewHolder`들이 같은 타입인 경우 캐싱이 되지않아 `Recycled Pool`로 넘어가서 제거되었다
2. 모든 `ItemView`들이 애니메이션이 존재할 때, `notifyItemRangeChanged(0, getItemCount())가` 호출되면서, 애니메이션이 끝나고 이 `ItemView`들을 `Recycled Pool`에 다시 추가하려고하는데,
이 때, R`ecycled Pool`은 이렇게 많은 아이템이 필요하지 않다고 판단하고 제거된다. - 이름 방지하기 위해선 다음 방법을 사용할 수 있다.
- 1. 조금 더 세밀하게 adapter를 업데이트하기 (ex:
notifyItemChanged(3)) - 2.
pool.setMaxRecycledViews(type, count)를 통해 pool의 크기를 늘리기
- 1. 조금 더 세밀하게 adapter를 업데이트하기 (ex:
RecycledViewPool
- 위에서 캐시와
RecycledPool을 탐색하며,RecyclerView가ViewHolder를 찾는 과정과,ViewHolder의 생성을 알 수 있었다. 그렇다면,RecycledViewPool이란 어떤 것일까?
public static class RecycledViewPool {
private static final int DEFAULT_MAX_SCRAP = 5;
public void setMaxRecycledViews(int viewType, int max) {
ScrapData scrapData = getScrapDataForType(viewType);
scrapData.mMaxScrap = max;
final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
while (scrapHeap.size() > max) {
scrapHeap.remove(scrapHeap.size() - 1);
}
}
public int getRecycledViewCount(int viewType) {
return getScrapDataForType(viewType).mScrapHeap.size();
}
@Nullable
public ViewHolder getRecycledView(int viewType) {
final ScrapData scrapData = mScrap.get(viewType);
if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
for (int i = scrapHeap.size() - 1; i >= 0; i--) {
if (!scrapHeap.get(i).isAttachedToTransitionOverlay()) {
return scrapHeap.remove(i);
}
}
}
return null;
}
RecycledViewPool은RecyclerView.Recycler의inner class이다.getRecycledView의parameter로ViewType을 전달하면,ViewType에 맞는ViewHolder를 return해준다는 것을 알 수 있고,ViewType마다ViewHolder Pool을 가지고 있다는 것을 알 수 있다.- 즉 캐시에서 원하는
ViewHolder를 찾지 못한 경우 마지막으로RecycledViewPool의getRecycledView로 해당ViewType에 해당하는ViewHolder를 달라고 요청하는 것이다 - 또한 상수로
DEFAULT_MAX_SCRAP =5로 선언되어 있는 것은ViewType별로 가지고 있는pool의 기본 용량이 5개라는 것이다 setMaxRecycledViews의parameter로ViewType과pool이 가지고 있는ViewHolder의 개수를 전달하면pool의 용량을 늘리거나 줄일 수 있다.- 이렇게
pool의 용량을 개발자가 직접 조절할 수 있다는 것은 매우 중요한데,
만약 화면에 동일한viewType을 가지는 아이템이 몇십개 존재하면 이들이 동시에 변경되어야 할땐 해당viewType을 가지는pool의 용량을 크게 설정하는 게 좋다.ViewHolder를 많이 저장해두면 재사용할 수 있는 ViewHolder도 많아지기 때문이다.
반면 화면에 딱 하나만 보여지는ViewType이 있다면 용량을 1로 설정하면 메모리를 절약할 수 있다
- 이렇게
- 또 하나 중요한 점은
RecycledViewPool가public으로 설정된class라는 것이다. 즉RecyclerView.RecycledViewPool()처럼RecycledViewPool객체를 생성해 해당 Pool을 '공유'할 수 있다.
→ 즉여러 RecyclerView들이같은 Pool을 공유해 메모리를 절약할 수 있다.
Dirty View
- 위처럼
pool에 있는View들을dirty view라고 부른다. dirty view는pool에 들어올 때View와ViewType만 남기고potition,flags등의 상태는 초기화 되기 때문에pool에 존재하는dirty view들을 꺼내 쓰려면 데이터를 다시 바인딩해주어야 한다.- 반면
pool이 아닌캐시에 있는view는position,flags등의 상태를 그대로 가지고 있기 때문에 바인딩없이 그대로 재사용할 수 있다.
Cache
- 원하는
ViewHoler가 있는지RecyclerdViewPool에서 찾기 전에Cache를 먼저 방문한다. Cache는ViewHolder로 이루어진리스트로,RecyclerdViewPool와 다르게viewType으로ViewHolder를 구분하지 않는다.- 대신 "
position"을 기준으로 탐색한다. 따라서캐시에 있는 ViewHolder는 데이터를 다시 바인딩 할 필요 없이 원래 위치해 있던position에 그대로 재사용 될 수 있는 것이다 - 예를 들어 가장 위에 있던
position 5아이템이 위로 스크롤 되어 화면에서 벗어난 후 다시 아래로 스크롤되어 화면에 보여질 때position 5에 해당하는ViewHolder가cache에 있었다면5라는 position을 다시 바인딩할 필요없이 바로 재사용 할 수 있다
- 대신 "
요약
즉, ViewHolder를 cache에서 찾았다면 → view 변경없이 바로 재활용
pool에서 찾았다면 → 바인딩 필요
ViewHolder가 어디에도 존재하지 않으면 → 새로 생성되고 바인딩됨
ViewHolder 생성의 시점과 개수
(RecyclerView의 동작방식)
RecyclerView가ViewHolder를 재활용하는 과정에서 가장 상단에 있는View가 사라지면 해당ViewHolder가 스크롤시 하단에 나타나는View에 바로 재활용된다고 생각하기 쉽다.- 그러나
ViewHolder는 "바로" 재활용 되지 않는다. - 가장 처음
RecyclerView가Adapter에set 되었을 때호출되는콜백함수를 보면RecyclerView가 화면에onAttachedToRecyclerView가 호출되어 붙음- 화면에 총 4개의
ViewHolder를 그리기 위한onCreateViewHolder호출 LayoutManager가addView를 호출해itemView를position에 잘 붙인 후RecyclerView에게 알리면RecyclerView는Adapter에게onViewAttachedToWindow호출

- 중요한건, 화면에 최초로 보이는 4개의 ViewHolder를 생성하고 난 후 스크롤을 내렸을 때
- 바로 위에 있는
ViewHolder인0번 holder가 사라지고5번째 itemView가 나타나면서0번째 ViewHolder를 재사용할 것이기 때문에 바로onCreateViewHolder를 건너뛰고onBindViewHolder를 호출할 것이라 생각한다. - 하지만
5번째 itemView가 나타나며 또ViewHolder를 생성한다.8번째 itemView가 나타날때까지ViewHolder는 재활용되지 않고 계속 생성된다.
→ 이는RecyclerView가 사라진ViewHolder를 '바로' 재활용하지 않기 때문에 나타나는 현상이다 - 계속 스크롤하다가
9번째 itemView가 만들어지는 순간 가장 먼저detached되었던0번째 ViewHolder가onViewRecycled되어 나타난다.onViewRecycled는"재활용할 홀더를 가지고 왔음"을 알리는 메서드이다. - 이후
9번째 itemView부터는onCreateViewHolder가 호출되지 않고detached되었던ViewHolder들이 차례로onViewRecycled되어 재사용된다.

- 정리하자면,
9번째 itemView가 나타났을 때가장 먼저 사라졌던 0번째 홀더가 재사용됨을 알리는onViewRecycled:0이 호출되고 이holder가9번째 홀더가 되어 bind되는 것이다.
→ 이는 바로ViewHolder를 재사용한다는 의미의 콜백인onViewRecycled가 호출되지 않는다는 것을 통해 알 수 있다 - 이후
RecyclerView를 파괴하게 되면 화면에서 사라짐과 동시에onDetachedFromRecyclerView가 호출된다.
RecyclerView Adapter 메소드 호출 순서
RecyclerView Adapter는 아래의 4개의 메소드를 통해서 데이터를 바인딩한다.onCreateViewHolderonBindViewHoldergetItemCountgetItemViewType
- 호출되는 순서를 살펴보면,
- 예를 들어 단말 화면에 10개의 리스트가 보인다면, 맨 처음
getItemCount()가 호출되면서 총 item의 갯수가 몇개인지 판단한다. getItemViewType()이 호출되면서, 현재Item View의Position에 해당하는ViewType이 무엇인지를 판단한다.onCreateViewHolder에서ViewType에 해당하는ViewHolder를 생성하여 리턴한다.
(ViewHolder는 각 Item View의 정보를 가지고 있는 애이기 때문에, Adapter는 계속해서 ViewHolder를 이용하여 Item View를 관리할 것이다. )onBindViewHolder에서는 생성된ViewHolder와Position을 전달받아서, 현재Position에 맞는data를ViewHolder가 관리하는View들에 binding한다.
- 예를 들어 단말 화면에 10개의 리스트가 보인다면, 맨 처음
→ getItemCount()가 1번 호출되고,
그 후 getItemViewType() → onCreateViewHolder() → onBindViewHolder()가 10번씩 호출될 것이다.
출처 및 참고자료
RecyclerView ins and outs - Google I/O 2016
RecyclerView Deep Dive with Google I/O 2016
Recycler View 제대로 이해하기 - RecyclerView lifecycle
[안드로이드 공식문서 파헤치기] RecyclerView의 모든 것! - 1편(구조, 탄생배경)
[안드로이드] Recyclerview 제대로 알고 쓰자 !
Pluu Dev - RecyclerView#ViewHolder에서 ViewTreeLifecycleOwner 사용법
'IT > Android' 카테고리의 다른 글
| [Android/Refactoring] Memory Leak - 1. 안드로이드 앱에서의 메모리 누수 찾기 (1) | 2023.07.21 |
|---|---|
| [Android] RecyclerView Deep Dive - 2. RecyclerView에서의 활용과 최적화 (0) | 2023.07.10 |
| [Android/FCM] (5) Android에서의 알림 수신 구현 (0) | 2023.03.11 |
| [Android/FCM] (4) Android 설정 (0) | 2023.03.07 |
| [Android/FCM] (3) Registration Token 관리 (0) | 2023.03.02 |