일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- android13
- rx
- MediaSession
- Reactive
- PagingLib
- mvvm
- node.js
- Animation
- SwiftUI Tutorial
- MediaPlayer
- databinding
- 테스트 자동화
- paging
- node
- liveData
- junit
- Koin
- Kotlin
- list
- SWIFTUI
- google play
- GCP
- MotionLayout
- 인앱결제
- 동영상
- Android
- Android 13
- RxKotlin
- Observable
- mysql
- Today
- Total
봄날은 갔다. 이제 그 정신으로 공부하자
Paging Library - Overview 본문
Paging Library를 사용하면 전체가 아닌 사용자가 지정한 묶음 단위로 한 번에 로드하여 표시할 수 있으며, 요청에 따라 일부 데이터를 로드하면 네트워크 대역폭 및 시스템 리소스 사용량을 줄일 수 있습니다.
이 글에서는 라이브러리의 몇 가지 개념적인 예를 제공하며 라이브러리의 작동 방식을 개략적으로 설명합니다.
Library Architecture
PagedList
Paging Library의 핵심 구성요소는 앱의 데이터 묶음 또는 페이지를 로드하는 PagedList 클래스입니다.
데이터가 더 많이 요구되면 데이터는 기존 PagedList 객체로 페이징되며 로드된 데이터가 변경되면 LvewData 또는 RxJava2 기반 객체에서 식별 가능한 데이터 홀더로 새로운 PagedList 인스턴스를 내보내고 PageList 객체가 생성되면 앱의 UI에 콘텐츠가 표시됩니다.
아래는 PagedList 객체의 LiveData 홀더를 사용하여 데이터를 로드하고 표시하도록 앱의 뷰 모델을 구성하는 방법입니다.
class ConcertViewModel(concertDao: ConcertDao) : ViewModel() {
val concertList: LiveData<PagedList<Concert>> =
concertDao.concertsByDate().toLiveData(pageSize = 50)
}
Data
각 PagedList 인스턴스는 상응하는 DataSource 객체에서 앱 데이터의 최신 스냅샷을 로드하고 Data는 앱의 백엔드 또는 데이터베이스에서 PagedList 객체로 이동합니다. 다음 예는 Room Persistence Library를 사용하여 앱의 데이터를 구성하지만 다른 방법을 사용하여 데이터를 저장하려면 사용자가 직접 source factory를 해도 무방합니다.
@Dao
interface ConcertDao {
// The Int type parameter tells Room to use a PositionalDataSource object.
@Query("SELECT * FROM concerts ORDER BY date DESC")
fun concertsByDate(): DataSource.Factory<Int, Concert>
}
UI
PagedList 클래스는 PagedListAdapter와 함께 작동하여 RecyclerView에 항목을 로드합니다. 이러한 클래스는 함께 작동하여 콘텐츠 로드 시 콘텐츠를 가져와서 표시하며 보이지 않는 콘텐츠를 미리 가져오고 콘텐츠 변경사항을 애니메이션 처리합니다.
다양한 Data Architecture 지원
Paging Library는 다음과 같은 데이터 아키텍처를 지원합니다.
- 백엔드 서버에서만 제공된 데이터 아키텍처
- 기기 내 데이터베이스에만 저장된 데이터 아키텍처
- 기기 내 데이터베이스를 캐시로 사용하는 다른 소스의 조합
네트워크 전용 또는 데이터베이스 전용 솔루션의 경우 데이터가 앱의 UI 모델로 직접 이동하고,
조합된 접근 방식을 사용하고 있다면 데이터가 백엔드 서버에서 기기 내 데이터베이스로 이동한 후 앱의 UI 모델로 이동합니다. 가끔은 각 데이터 흐름의 엔드포인트에서 로드할 데이터가 부족할 때도 있는데 그 시점에 엔드포인트는 데이터를 제공한 구성요소에 추가 데이터를 요청합니다. 예를 들어 기기 내 데이터베이스에서 데이터가 부족하면 데이터베이스는 서버에 추가 데이터를 요청합니다.
Network Only
백엔드 서버의 데이터를 표시하려면 동기 버전의 Retrofit API를 사용하여 사용자 Custom DataSource Object에 정보를 로드해야 합니다.
Database Only
가급적이면 Room Persistence Library를 사용하여 로컬 저장소를 관찰하도록 RecyclerView를 설정합니다. 이렇게 하면 데이터가 앱의 데이터베이스에 삽입되거나 수정될 때마다 변경사항이 데이터를 표시하는 RecyclerView에서 자동으로 반영됩니다.
Network and Database
데이터베이스 관찰을 시작했으면 PagedList.BoundaryCallback을 사용하여 데이터베이스에서 데이터가 부족한 시기를 수신 대기할 수 있습니다. 그러면 네트워크에서 추가 항목을 가져와 데이터베이스에 삽입할 수 있게 된다. UI가 데이터베이스를 관찰하고 있다면 이렇게만 하면 됩니다.
Network 오류 처리
Paging Library를 사용하여 표시되는 데이터를 가져오거나 페이징하는데 네트워크 사용 시 아래와 같은 연결 취약이 발생하므로 이에 대한 처리가 중요합니다.
- 특정 서버가 네트워크 요청에 응답하지 못할 수 있습니다.
- 기기가 느리거나 취약한 네트워크에 연결되었을 수 있습니다.
네트워크를 사용할 수 없는 상황에서는 앱에서 각 요청이 실패했는지 확인하고 최대한 정상적으로 복구해야 합니다.
예를 들어 데이터 새로고침 단계가 작동하지 않으면 사용자가 선택할 수 있는 '다시 시도' 버튼을 제공할 수 있다. 데이터 페이징 단계에서 오류가 발생하면 페이징 요청을 자동으로 다시 시도하는 것이 가장 좋습니다.
기존 앱 업데이트
앱이 이미 데이터베이스 또는 백엔드 소스의 데이터를 사용하고 있다면 Paging Library에서 제공하는 기능으로 직접 업그레이드할 수 있다. 이 섹션에서는 일반적인 기존 디자인이 있는 앱을 업그레이드하는 방법을 보여줍니다.
Custom paging solutions
앱의 데이터 소스에서 소규모 데이터 하위 집합을 로드하는 데 맞춤 기능을 사용하면 로직을 PagedList 클래스의 로직으로 바꿀 수 있으며 PagedList 인스턴스는 기본적으로 일반 데이터 소스의 연결을 제공한다. 또한 이러한 인스턴스는 앱의 UI에 포함할 수 있는 RecyclerView 객체의 어댑터도 제공합니다.
page 대신 list를 사용하여 로드된 데이터
메모리 내 목록을 UI 어댑터의 백업 데이터 구조로 사용한다면 목록의 항목 수가 많아질 수 있을 때 PagedList 클래스를 사용하여 데이터 업데이트를 관찰하는 것이 좋습니다.
PagedList 인스턴스는 LiveData<PagedList> 또는 Observable<List>를 사용하여 앱의 UI에 데이터 업데이트를 전달함으로써 로드 시간과 메모리 사용량을 최소화할 수 있습니다. 앱에서 List 객체를 PagedList 객체로 바꿀 때 앱의 UI 구조 또는 데이터 업데이트 로직을 변경할 필요가 없다는 점도 좋습니다.
CursorAdapter를 사용하여 data cursor를 list view와 연결
CursorAdapter를 사용하여 Cursor의 데이터를 ListView와 연결할 수 있습니다. 이런 상황에서는 일반적으로 데이터를 ListView에서 앱의 목록 UI 컨테이너인 RecyclerView로 이전한 후 Cursor 인스턴스가 SQLite 데이터베이스에 액세스하는지 여부에 따라 Cursor 구성요소를 Room 또는 PositionalDataSource로 바꿔야 합니다.
Spinner 인스턴스 관련 작업 시와 같은 일부 상황에서는 어댑터 자체만 제공하며 라이브러리가 어댑터에 로드된 데이터를 가져와 표시합니다. 이러한 상황에서는 어댑터의 데이터 유형을 LiveData<PagedList>로 변경한 다음 먼저 ArrayAdapter 객체에서 목록을 래핑한 후 라이브러리 클래스가 UI에서 항목을 확장하도록 합니다.
AsyncListUtil을 사용하여 컨텐츠를 비동기적으로 로드
AsyncListUtil 객체를 사용하여 정보 그룹을 비동기적으로 로드 및 표시하고 있다면 다음과 같이 Paging Library를 통해 데이터를 더 쉽게 로드할 수 있습니다.
- 데이터가 특정 위치에 있지 않아도 됩니다.
Paging Library를 사용하면 네트워크에서 제공하는 키를 사용해 백엔드에서 직접 데이터를 로드할 수 있습니다.
- 데이터가 셀 수 없을 정도로 많아도 됩니다.
Paging Library를 사용하면 남은 데이터가 없어질 때까지 데이터를 페이지에 로드할 수 있습니다.
- 데이터를 더 쉽게 관찰할 수 있습니다.
Paging Library는 앱의 ViewModel이 보유하는 데이터를 식별 가능한 데이터 구조로 표시할 수 있습니다.
Database 예시
아래 두 개의 코드 예시들은 모든 요소가 함께 작동되도록 하는 몇 가지 가능한 방법을 설명합니다.
LiveData를 사용하여 페이징된 데이터 관찰
아래 코드 예시는 모든 요소가 함께 작동하는 것을 설명합니다. Concert 이벤트가 데이터베이스에서 추가, 삭제 또는 변경될 때 RecyclerView의 콘텐츠가 자동으로 그리고 효율적으로 업데이트됩니다.
@Dao
interface ConcertDao {
// The Int type parameter tells Room to use a PositionalDataSource
// object, with position-based loading under the hood.
@Query("SELECT * FROM concerts ORDER BY date DESC")
fun concertsByDate(): DataSource.Factory<Int, Concert>
}
class ConcertViewModel(concertDao: ConcertDao) : ViewModel() {
val concertList: LiveData<PagedList<Concert>> =
concertDao.concertsByDate().toLiveData(pageSize = 50)
}
class ConcertActivity : AppCompatActivity() {
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val viewModel = ViewModelProviders.of(this).get<ConcertViewModel>()
val recyclerView = findViewById(R.id.concert_list)
val adapter = ConcertAdapter()
viewModel.livePagedList.observe(this, PagedList(adapter::submitList))
recyclerView.setAdapter(adapter)
}
}
class ConcertAdapter() :
PagedListAdapter<Concert, ConcertViewHolder>(DIFF_CALLBACK) {
fun onBindViewHolder(holder: ConcertViewHolder, position: Int) {
val concert: Concert? = getItem(position)
// Note that "concert" is a placeholder if it's null.
holder.bindTo(concert)
}
companion object {
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Concert>() {
// Concert details may have changed if reloaded from the database,
// but ID is fixed.
override fun areItemsTheSame(oldConcert: Concert,
newConcert: Concert) = oldConcert.id == newConcert.id
override fun areContentsTheSame(oldConcert: Concert,
newConcert: Concert) = oldConcert == newConcert
}
}
}
RxJava2를 사용하여 페이징된 데이터 관찰
LiveData 대신 Rxjava2 사용을 선호하면 다음과 같이 Observable 또는 Flowable 객체를 대신 생성할 수 있습니다.
class ConcertViewModel(concertDao: ConcertDao) : ViewModel() {
val concertList: Observable<PagedList<Concert>> =
concertDao.concertsByDate().toObservable(pageSize = 50)
}
아래 코드를 사용하여 테이터를 관찰을 시작하고 중지할 수 있습니다.
class ConcertActivity : AppCompatActivity() {
private val adapter: ConcertAdapter()
private lateinit var viewModel: ConcertViewModel
private val disposable = CompositeDisposable()
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val recyclerView = findViewById(R.id.concert_list)
viewModel = ViewModelProviders.of(this).get<ConcertViewModel>()
recyclerView.setAdapter(adapter)
}
override fun onStart() {
super.onStart()
disposable.add(viewModel.concertList.subscribe(adapter::submitList)))
}
override fun onStop() {
super.onStop()
disposable.clear()
}
}
RxJava2 기반 솔루션과 관련된 ConcertDao 및 ConcertAdapter의 코드는 LiveData 기반 솔루션과 관련된 코드와 동일합니다.
'Android jetpack' 카테고리의 다른 글
Paging Library - Display paged lists (0) | 2020.11.24 |
---|---|
Paging Library - Gather Paged Data (0) | 2020.11.24 |
WorkManager 알아보기 (0) | 2020.11.23 |
Android 백그라운드 처리 가이드 (0) | 2020.11.23 |
의존성 주입 라이브러리 - 2편 (Dagger Hilt) (0) | 2020.11.23 |