안녕하세요.
안드로이드 Jetpack Library 중 하나인 Paging에 대해 알아보겠습니다.
Paging?
대용량 데이터(로컬 데이터베이스 or 서버 데이터)를 페이지 별로 로드하고 표시할 수 있게 해주는 라이브러리 입니다.
데이터를 한번에 전부 다 들고오는 것이 아닌,
일정 부분만 들고온 뒤에 필요할 때마다 다음 일정 부분을 들고오게 해줍니다.
대표적으로 구글 검색 엔진이 있습니다.
장점
대용량 데이터를 처리하는 앱에서 모든 데이터를 한 번에 로드하면 아래와 같은 문제점이 발생합니다.
- 메모리 부족 문제
- 초기 로딩 시간이 길어짐
Paging을 쓰면 위와 같은 문제를 해결할 뿐만 아니라, 아래와 같은 장점들도 얻을 수 있습니다.
- 메모리 관리
- 현재 화면에 보이는 데이터만 로드하기 때문에 메모리 누수를 방지함
- 빠르고 부드러운 스크롤
- 스크롤 할 때 필요한 데이터만 로드되므로 스크롤이 더 부드럽고 빠름
- 무한 스크롤
- 네트워크 트래픽 관리
구성요소
안드로이드 공식 문서에서는 아래와 같이 레이어를 나누고 그 안에 여러 요소들을 정의했습니다.
PagingSource
특정 페이지 쿼리의 데이터 청크를 로드하는 기본 클래스입니다.
데이터 소스 및 소스에서 데이터를 가져오는 방법에 대해 정의합니다.
Data Layer에 속합니다.
class ArticlePagingSource : PagingSource<Int, Article>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Article> {
...
return LoadResult.Page(
data = data,
prevKey = params.key?.minus(1),
nextKey = params.key?.plus(1)
)
}
override fun getRefreshKey(state: PagingState<Int, Article>): Int? {
val anchorPosition = state.anchorPosition ?: return null
val article = state.closestItemToPosition(anchorPosition) ?: return null
return ensureValidKey(key = article.id - (state.config.pageSize / 2))
}
}
<Key, Value> 형태
Key : 데이터를 로드하는데 사용하는 식별자 번호
Value : 데이터 클래스
Override Function
- load
- 특정 페이지에 대한 데이터를 검색하고 반환하는 역할을 합니다.
- prevKey, nextKey로 이전 페이지, 다음 페이지를 연결시킵니다.
- getRefreshKey
- UI 관련 항목을 새로고침해야 할 때 호출합니다.
- 새로고침하면 -> PagingSource의 데이터가 변경 -> PagingSource를 새로 만들고 -> PagingData를 내보내 UI에 알리게 되는 구조입니다.
PagingConfig
페이징 동작을 결정하는 매개변수를 정의하는 클래스입니다.
페이지 크기, 자리표시자의 사용 여부 등을 지정할 수 있습니다.
자리표시자
- 임시로 채워넣는 내용물
- 데이터 로드 전 로딩 중 등과 같은 것
class ArticleViewModel(
repository: ArticleRepository,
) : ViewModel() {
val items: Flow<PagingData<Article>> = Pager(
config = PagingConfig(pageSize = ITEMS_PER_PAGE, enablePlaceholders = false),
pagingSourceFactory = { repository.articlePagingSource() }
)
...
}
PagingConfig 클래스로 pageSize와 enablePlaceholders를 설정할 수 있습니다.
이 밖에 다른 options 들도 설정해줄 수 있습니다.
Pager
PagingData 스트림을 생성하는 클래스입니다.
=> Flow를 통해 UI에 전달합니다.
아래와 같은 이유로 ViewModel에서 만드는 것을 권장합니다.
- 라이프 사이클 관리
- UI 라이프 사이클과 연결되어 있어서 데이터를 관리하기 좋은 위치
- 구성 변경 시 데이터 보존
- 구성 변경 (화면 회전 등) 에도 데이터를 보존해준다는 장점
class ArticleViewModel(
repository: ArticleRepository,
) : ViewModel() {
val items: Flow<PagingData<Article>> = Pager(
...
)
.flow
.cachedIn(viewModelScope)
}
Pager 클래스와 flow를 활용해 viewModel에서 데이터 스트림을 만들어 UI에 전달합니다.
PagingData
페이지로 나눈 데이터의 컨테이너입니다.
데이터를 새로고침할 때마다 자체 PagingSource로 지원되는 상응하는 PagingData 내보내기가 별도로 생성됩니다.
class ArticleViewModel(
repository: ArticleRepository,
) : ViewModel() {
val items: Flow<PagingData<Article>> = Pager(
...
)
...
}
Article 데이터를 감싸는 데이터 컨테이너 입니다.
PagingData<Article>로 쓰인 부분을 확인할 수 있습니다.
PagingDataAdapter
RecyclerView에 PagingData를 표시하는 RecyclerView.Adapter 서브 클래스입니다.
내부 PagingData 로드 이벤트를 수신 대기하고, 페이지가 로드될 때 UI를 효율적으로 업데이트 합니다.
class ArticleAdapter : PagingDataAdapter<Article, ArticleViewHolder>(ARTICLE_DIFF_CALLBACK) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArticleViewHolder =
ArticleViewHolder(
ArticleViewholderBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false,
)
)
override fun onBindViewHolder(holder: ArticleViewHolder, position: Int) {
...
}
}
RecyclerView에 바인딩하기 위해 Adapter를 사용합니다.
ViewHolder를 만들고 바인드 하는 함수 두개를 오버라이드해서 쓸 수 있습니다.
여기까지 Paging에 대한 기본적인 것을 알아봤습니다.
다음 글에서는 코드와 함께 사용 방법에 대해 알아보겠습니다.
Reference
Android Paging 기본사항 | Android Developers
Android Paging 기본사항 | Android Developers
이 Codelab에서는 목록을 표시하는 앱에 Paging 라이브러리를 통합합니다. Paging 라이브러리를 사용하면 로컬 저장소에서나 네트워크를 통해 대규모 데이터 세트의 데이터 페이지를 로드하고 표시
developer.android.com
'Android Library' 카테고리의 다른 글
[Android Library] Retrofit (1) - 서버와 통신 (0) | 2023.11.16 |
---|---|
[Android Library] Paging (2) - 프로젝트에 적용해보기 (1) | 2023.10.30 |
[Android Library] Hilt (3) - hilt in MVVM (0) | 2023.10.18 |
[Android Library] Hilt (2) - 기본 사용법 (2) | 2023.10.17 |
[Android Library] Hilt (1) - 의존성 주입 (Dependency Injection) (1) | 2023.10.16 |