Android Paging Library in Jetpack — 1. Overview

아이비
12 min readSep 16, 2018

--

이 글은 아래의 Paging Library Google I/O 2018 세션과 관련 codelab 및 공식문서의 내용을 정리한 것입니다.

Android Jetpack: manage infinite lists with RecyclerView and Paging (Google I/O ‘18)

일반적인 앱을 만들 때 list UI 구현은 흔한 일 중에 하나입니다. 그리고 데이터가 누적되어 많아질 경우를 고려하여 paging 처리도 하셨을 것입니다. Paging Library는 이름에서 짐작하실 수 있듯이 list의 paging 처리를 더욱 쉽게 구현할 수 있도록 도와주는 library 입니다. 어떤 점에서 기존의 구현과 달라지는지 알아보겠습니다.

1. Paging Library — Data Components

(1) PagedList

  • 페이지에서 데이터를 비동기적으로 load하는 collection 입니다.
  • DataSource class가 PagedList가 필요로 하는 데이터를 제공하고, list를 업데이트할 수 있도록 도와줍니다.
PagedList 와 DataSource

(2) DataSource

  • 데이터의 snapshots을 PagedList에 load 하는 base class 입니다.
  • subclass로 PositionalDataSource, ItemKeyedDataSource, PagedKeyedDataSource가 있습니다.

(3) DataSource.Factory

  • DataSource를 생성합니다.

(4) LivePagedListBuilder

  • DataSource.Factory와 PagedList.Config를 기반으로 LiveData <PagedList> 를 생성합니다.

(5) 정리 : data를 load한 후, update 까지의 과정

  • page size는 앱의 요구사항에 따라 다르게 설정할 수 있어야 합니다. 즉, PagedList는 DataSource에 필요한 data만큼 요청할 수 있어야 합니다.
DataSource의 역할
  • 그리고 이 DataSource를 DataSource Factory로 감쌀 것입니다. 새로운 PagedList가 필요할 때마다 DataSource를 생성할 것입니다.
  • DataSource Factory는 현재의 snapshot을 invalid로 만드는 data update가 발생할 때, 새로운 PagedList를 생성합니다. DataSource가 invalid된 상태에서 더 많은 data를 가져올 유일한 방법이 DataSource Factory에서 새로운 DataSource를 생성하는 것입니다.
  • 만약 update 되지 않는 페이지를 구현한다면, 단일 DataSource를 생성하고 단일 PagedList에 data를 전달하면 됩니다.
DataSouce Factory와 DataSource의 관계
  • 그리고 ViewModel에 data를 load하는 방법을 정의합니다. Database에 선언한 DataSource Factory를 가져와서 LivePagedListBuilder를 사용해 LiveData를 생성합니다.
class MyViewModel(val userDao : UserDao) : ViewModel() {
val users: LiveData<PagedList<User>>
init {
val factory: DataSource.Factory = database.allUsersFactory()
users = LivePagedListBuilder(factory, page_size).build()
}

2. Paging Library — UI Components

(1) PagedListAdapter

  • RecyclerView의 PagedLists에서 페이징된 데이터를 표시하는 RecyclerView.Adapter 입니다.
  • DiffUtil Callback의 구현 : page가 load될 때 callback을 수신하고, background thread에서 DiffUtil을 사용해 새로운 PagedList 의 update 내용을 계산합니다.

(2) BoundaryCallback

  • PagedList가 사용 가능한 data의 끝에 도달했을 때 signal을 보냅니다.

(3) 정리

  • PagedListAdapter는 ListAdapter와 정확히 같습니다. PagedListAdapter는 page list에서 content가 update될 때 page loading을 처리합니다.
  • PagedListAdapter 구현의 유일하게 달라진 점은 content로 표시할 객체가 nullable이 될 수 있다는 점입니다. 이는 아래의 PagedList.Config class의 placeholder attribute와 관련이 있습니다.

3. Configuration — PagedList.Config class

(1) attribute

  • Page size : page에 로드할 item 갯수
  • InitialLoadSizeHint : 기본값은 page size의 3배 입니다. 이는 initial load 후에 initial page fetch를 피하기 위해 initial load 값보다 더 크게 설정하는 것입니다.
  • Prefetch distance : 기본값은 page size 입니다. 이는 현재 load한 것으로부터 얼마나 더 많이 load할 것인지 설정하는 것입니다. 이 값을 50으로 설정한다면, 이미 접근한 data보다 50개의 item을 더 load 할 것입니다.
  • Placeholder presence : 기본값은 true 입니다. Placeholder는 first page를 load할 때 기대할 수 있는 기본 페이징입니다. Placeholder가 true인 경우의 동작은 다음과 같습니다. entire dataset을 RecyclerView에 표시하기 때문에 scroll bar의 크기는 약간 작습니다. scroll 하는 동안, data를 더 load하면서 scroll bar가 jumping하는 현상은 없습니다. 그리고 scroll 하면서 load되지 않은 item이 있다면, adapter에서 null로 표시됩니다. null로 표시됐던 item들도 결국 load된 후 표시되므로 animation 효과를 얻을 수 있습니다.
Placeholder가 true인 경우, load 되지 않은 item은 null로 표시됐다가 load한 후에 그 위치에 표시된다.

(2) Placeholder의 장점

  • Support for scrollbars : adapter가 목록의 전체 크기를 알고 있는 scrollbar를 그립니다. scrollbar는 즉시 보이고, 모든 dataset이 RecyclerView에 표시되어 빠른 scrolling이 가능합니다.
  • No loading spinner necessary : 사용자가 placeholder를 볼 수 있기 때문에 하단에 loading spinner를 구현할 필요가 없습니다. 사용자가 gray icon을 볼 수 있고 해당 item이 여전히 load 되고 있음을 알 수 있기 때문입니다.

(3) Placeholder 의 전제 조건

  • Countable data set : DataSouce는 item을 셀 수 있어야 합니다. Room은 item을 셀 수 있지만, 네트워크에서 data를 load하는 경우 dataset을 구성하는 item의 갯수를 결정하는 것이 어려울 수도 있습니다.
  • Adapter must handle null items : load되지 않은 item에 대한 처리가 필요합니다. data를 ViewHolder에 binding할 때 load되지 않은 data를 나타내는 기본값을 제공해야 합니다.
  • Item should be same size : 예를 들어 임의의 양의 text가 있는 item의 높이를 추측할 수 없다면(혹은 크기가 변경될 수 있는 경우), crossfade animation 효과가 좋지 않습니다. 이 경우에는 placeholder를 사용하지 않도록 강력히 제안합니다.

4. Paging Library 의 작동 방식

(1) Repository side — PagedList 생성

Paging Library에서 data를 생성하는 과정
  • Repository는 application에서 data를 load 하는 부분을 나타냅니다.
  • LivePagedListBuilder.build 는 UI에 information funnel로 사용되는 LiveData 뿐만 아니라 data를 생성하는 쪽도 만들 수 있습니다.
  • LiveData를 observing 하면 새로운 PagedList를 생성할 수 있습니다. 이를 위해 DataSourceFactory가 새로운 DataSource를 만듭니다. 이제 page list 를 넘길 준비를 합니다. Background thread에서 data를 initialize 하고 PagedList를 만듭니다.

(2) UI side — PagedList 노출

생성한 PagedList를 UI thread로 전달하는 과정
  • 생성한 첫번째 PagedList를 UI thread로 보냅니다. 그리고 PagedListAdapter에서 submitList를 호출하면 새로운 list를 설정해서 adapter에 보냅니다.
PagedListAdater에서 PagedList item 노출
  • adapter는 즉시 item을 보여주기 시작합니다. 하지만, 사용자가 scroll할 때 더 많은 data를 load해야 할수도 있습니다.
PagedList가 다음 page에 보여질 data를 가져오는 과정
  • 이 때, PagedList가 내부적으로 DataSource에서 data load를 trigger 하고 해당 data를 PagedList에 추가합니다.

(3) Data update — Repository side와 UI side의 communication

data가 update되는 과정 1 — 이전의 DataSource invalidate
  • item이 update 되면, DataSource.Factory는 이전 DataSource를 invalidate 합니다. 그리고 이 DataSource 에서 load하던 것을 즉시 중단하고, invalidate 시킵니다.
data가 update되는 과정 2 — 새로운 DataSource, PagedList 생성
  • 동시에, 새로운 PagedList를 생성해야할 시점을 알기 위해 알림을 듣고 있습니다. 우리는 이를 통해 새로운 DataSource를 생성해서 보내줄 새로운 PagedList를 만들 수 있습니다.
data가 update되는 과정 3 — PagedList에 새로운 data추가
  • initial data를 load할 때 adapter에 신호를 보내는 loading position을 기준으로 초기화하도록 신경써야 합니다. 그림에서 두번째 page로 load하는 data 이므로 PagedList의 위치도 두번째로 설정해야 한다는 의미입니다.
data가 update되는 과정 4— 기존의 PagedList와의 차이 계산
  • UI thread에 다시 PagedList를 보냅니다. 하지만 2개의 다른 list가 있기 때문에 background thread에서 asynchronous diff 를 계산합니다.
data가 update되는 과정 5— update가 필요한 item만 변경
  • diff를 계산하면, DiffResult를 RecyclerView로 전달하고 새로운 PagedList로 교환합니다. 즉시 새로운 item이 나타나고 crossfade animation이 보여집니다.

지금까지 Paging library의 components와 작동 방식을 살펴보았습니다. 다음 글에서는 DataSource의 subclass — PositionalDataSource, ItemKeyedDataSource, PagedKeyedDataSource에 대해 알아보겠습니다. 각각의 DataSource는 data를 요청하는 key값을 position, item의 속성, page로 지원하는 subclass 입니다.

다음 글 : Android Paging Library in Jetpack — 2. DataSource Type (예정)

Sign up to discover human stories that deepen your understanding of the world.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

아이비
아이비

Written by 아이비

원밀리언라인즈코딩 CEO

No responses yet

Write a response