RecyclearView의 ViewPool을 공유하여 Inflate 횟수를 극적으로 감소

9125 단어 RecyclerViewAndroid
Drid Kaigi 2018, 수고하셨습니다.
올해는 두 번째 참가인데 역시 일본 안드로이드 엔지니어의 명절이라 기분이 좋다.
이 글에는 Drid Kaigi 2018 공식 애플리케이션에 게시된 Recycled ViewPool의 공유 PR이 적혀 있다.

계기.


이번 드라이드 카이지@thagikura의 강연인'Deep dive into LayoutManager for RecryclearView'에서 RecyclearView의 ViewPool을 공유할 수 있다는 것을 알게 되었다.
그리고 공유가 가능하다는 것은 ViewPager의 각 페이지에 표시된 View가 대체적으로 같은 화면에서 공유된다면 다양한 효율화를 실현할 수 있지 않을까?그래서 조사를 시작했습니다.
어쨌든 RecyclearView 문서부터 보시면 됩니다.
  • RecyclerView#setRecycledViewPool
  • 여기 이렇게 써있어요.
    Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views. This can be useful if you have multiple RecyclerViews with adapters that use the same view types, for example if you have several data sets with the same kinds of item views displayed by a ViewPager.
    바로 상술한 상황에서 사용하는 기능이다.
    처음에 나는 우리 회사의 응용 프로그램으로 시험해 보고 싶었지만 문제가 있어서 (후술) 포기할 수밖에 없었다.
    하지만 눈앞에 검증하기 좋은 구조의 앱(Drid Kaigi 2018 공식 앱)이 있다는 것을 알아차리고 어렵게 이 앱이 생겨서 검증과 홍보를 해봤습니다.

    이루어지다


    공유 ViewPool의 실현 자체는 매우 간단하지만 공유의 실례RecycledViewPoolRecyclerViewsetRecycledViewPool에 맡긴다.
    하지만 이런 효과를 거두지 못했다.
    조금만 더 조사해 보면 LinearLayoutManagerGridLayoutManager에는 setRecycleChildrenOnDetach 이런 방법이 있다는 것을 알 수 있다.
  • LinearLayoutManager#setRecycleChildrenOnDetach
  • Set whether LayoutManager will recycle its children when it is detached from RecyclerView.
    If you are using a RecyclerView.RecycledViewPool, it might be a good idea to set this flag to true so that views will be available to other RecyclerViews immediately.
    Note that, setting this flag will result in a performance drop if RecyclerView is restored.
    이 로고를 설정하지 않으면 폐기된 RecyclearView의 목록 항목의 View는 다른 RecyclearView에서 바로 사용할 수 없습니다.
    이 로고도 함께 설정하면 다음과 같은 내용을 실현할 수 있습니다.
    binding.sessionsRecycler.apply {
        // 省略
        recycledViewPool = sharedRecycledViewPool
        (layoutManager as LinearLayoutManager).recycleChildrenOnDetach = true
    }
    
    conference-app-2018/RoomSessionsFragment.kt

    확인


    AllSessionsFragment.kt,RoomSessionsFragment.kt,ScheduleSessionsFragment.kt 3개의 Fragment에 공유 Pool을 설치한 후GroupAdapter를 다음 inflate로 교체한 횟수를 실험했다.
    class InflateCounterGroupAdapter : GroupAdapter<ViewHolder>() {
    
        companion object {
            private val count: AtomicInteger = AtomicInteger(0)
        }
    
        override fun onCreateViewHolder(parent: ViewGroup, layoutResId: Int): ViewHolder {
            Timber.d("Inflate view holder ${count.incrementAndGet()}")
            return super.onCreateViewHolder(parent, layoutResId)
        }
    }
    
    ViewPool 공유 전후로 inflate가 몇 차례 진행됐는지 계산했다.
  • 어플리케이션 시작(Room 모드의 All 태그 열기)
  • Room1 태그에서 Room7 태그로 왼쪽에서 오른쪽으로 차례로 클릭하여 페이지 전환
  • Action 표시줄의 메뉴 클릭에서 Schedule 모드로 전환(검증 시 Day2/14:00 탭 열기)
  • Day2/14:00 탭에서 All 탭으로, 오른쪽에서 오른쪽으로 순차적으로 가볍게 두드려 페이지 전환
  • 결실


    Pool 공유 없음
    Pool 공유
    inflate 횟수
    95
    31
    인플레이트 횟수가 약 1/3이라니!!
    Drid Kaigi 응용 프로그램처럼 거의 같은 종류의 View를 표시하는 RecyclearView에 Pool을 적용하여 공유하면 상당한 효과가 있다는 것을 알 수 있습니다.
    (물론 RecyclearView에 표시된 View의 종류수에 따라 효과의 정도가 달라질 수 있음)

    주의점


    Pool을 공유하는 RecyclearView의 어댑터에서 ViewType을 일치시킵니다


    당사 애플리케이션에 바로 적용할 수 없는 이유가 바로 이것입니다.
    RecrycledviewPool에서 사용 가능한 ViewHolder를 가져오면 Adapter가 getItemViewType 되돌아오는 ViewType을 관건으로 합니다.
    따라서 Pool을 공유하는 RecyclearView 사이에 다른 View에 동일한 ViewType을 할당하면 기대와 다른 View가 반환되어 완전히 붕괴됩니다.
    본사에서 사용하는 RecyclearView입니다.어댑터의 확장 라이브러리에서 내부에 ViewType이 동적으로 분배되어 울면서 포기했습니다.
    DridKaigi 응용 프로그램에서 사용하는 Groupie는View의layout id를 ViewType으로 하는 것이므로 안심하고 사용할 수 있습니다.

    RecredViewPool 유지 및 공유 방법 주의


    RecrycledViewPool 자체에는 Context가 없지만 내부'View 유지'='Activity의 Context 유지'.
    따라서 공유할 수 있는 것은 기본적으로 같은 Activity의 View (서로 다른 Activity 사이에서 공유하려고 시도할 때 어떤 일이 일어날지 검증되지 않음) 뿐이다.
    또 렉시트뷰폴의 생존 기간이 액티비티보다 길면 메모리 유출이 발생할 수 있다.
    틀리더라도 단식을 유지하고 공유하지 말아야 한다.
    애초 Drid Kaigi 앱에 게시된 PR은 View Model에서 유지됐지만, 이것도 절대 하지 말아야 한다.
    ref: https://developer.android.com/topic/libraries/architecture/viewmodel.html
    Caution: A ViewModel must never reference a view, Lifecycle, or any class that may hold a reference to the activity context.

    이번에 나온 PR.

  • 공유 Pool:
  • https://github.com/DroidKaigi/conference-app-2018/pull/660
  • ViewModel에서 유지되는 Pool을 Dagger로 Injection하는 방식으로 변경합니다.
  • https://github.com/DroidKaigi/conference-app-2018/pull/662
  • 끝맺다


    올해는 업무와 사생활이 바빠 Drid Kaigi 이전에는 앱 발표를 전혀 할 수 없었지만 결국 긴급 홍보를 했다.
    ViewPager를 사용하여 동일한 항목을 표시하는 응용 프로그램은 필수입니까?그런 앱이 많잖아요?생활 방식은요?🤔 https://t.co/HVfgMQV0RG — takahirom (@new_runnable) February 9, 2018
    응용 리더@takahirom가 말한 바와 같이 Pool 공유에 효과적인 응용 프로그램이 매우 많습니다. 효과는 매우 좋지만 설치하기가 매우 간단합니다. 반드시 시도해 보십시오.
    끝!

    좋은 웹페이지 즐겨찾기