Paging을 공부하다 보면 나오는 remoteMediator입니다.
1. remoteMediator를 왜쓰는가
This time, we’re dialing things up a notch. Till now we’ve been pulling our data directly from the network which works only in the best of circumstances. We may sometimes be on a slow internet connection, or have lost connection entirely. Even if our connection is good, we certainly don’t want our app to be a data hog as re-fetching data every time you navigate into a screen is wasteful.
The solution to these issues is to have a local cache we pull from, and refresh only when necessary. Updates to the cache should always hit the cache first, and then be propagated to the ViewModel. This way the local cache is the single source of truth. Conveniently for us, the Paging Library has this covered with a little help from the Room library! Let’s get into it!
네트워크 환경이 불안정할 때나 매번 데이터를 로드하는건 낭비니깐 remoteMediator를 사용해서 Room에 저장해놓고 씁니다.
2. 어떻게 쓰는가
구성항목을 일부 보겠습니다.
RemoteMediator의 기본 역할은 Pager가 데이터를 모두 사용했거나 기존 데이터가 무효화되었을 때 네트워크에서 더 많은 데이터를 로드하는 것입니다. RemoteMediator는 로드 동작을 정의하기 위해 재정의해야 하는 load() 메서드를 포함합니다.
일반적인 RemoteMediator 구현에는 다음 매개변수가 포함됩니다.
- query: 백엔드 서비스에서 검색할 데이터를 정의하는 쿼리 문자열
- database: 로컬 캐시의 역할을 하는 Room 데이터베이스
- networkService: 백엔드 서비스의 API 인스턴스
@OptIn(ExperimentalPagingApi::class)
class ExampleRemoteMediator(
private val query: String,
private val database: RoomDb,
private val networkService: ExampleBackendService
) : RemoteMediator<Int, User>() {
val userDao = database.userDao()
override suspend fun load(
loadType: LoadType,
state: PagingState<Int, User>
): MediatorResult {
// ...
}
}
load() 메서드의 반환 값은 MediatorResult 객체입니다. MediatorResult는 MediatorResult.Error(오류 설명 포함) 또는 MediatorResult.Success(로드할 데이터가 더 있는지 여부를 나타내는 신호 포함)일 수 있습니다.
- 로드가 성공적이고 수신된 항목 목록이 비어 있지 않으면 목록 항목을 데이터베이스에 저장하고 MediatorResult.Success(endOfPaginationReached = false)를 반환합니다. 데이터가 저장된 후에는 데이터 소스를 무효화하여 페이징 라이브러리에 새 데이터를 알립니다.
- 로드가 성공적이고 수신된 항목 목록이 비어 있거나 마지막 페이지 색인인 경우 MediatorResult.Success(endOfPaginationReached = true)를 반환합니다. 데이터가 저장된 후에는 데이터 소스를 무효화하여 페이징 라이브러리에 새 데이터를 알립니다.
- 요청에서 오류가 발생한 경우 MediatorResult.Error를 반환합니다.
RemoteMediator 구현은 또한 initialize() 메서드를 재정의하여 캐시된 데이터가 오래되었는지 점검하고 원격 새로고침을 트리거할지 결정할 수 있습니다. 이 메서드는 로드하기 전에 실행되므로 로컬 또는 원격 로드를 트리거하기 전에 데이터베이스를 조작할 수 있습니다(예: 기존 데이터 삭제).
initialize()는 비동기 함수이므로 데이터를 로드하여 데이터베이스에 있는 기존 데이터의 관련성을 확인할 수 있습니다. 가장 일반적인 사례는 캐시된 데이터가 특정 기간에만 유효한 경우입니다. RemoteMediator는 만료 기간이 지났는지 확인할 수 있으며, 이런 경우 페이징 라이브러리는 데이터를 완전히 새로고침해야 합니다. initialize() 구현은 다음과 같이 InitializeAction을 반환해야 합니다.
- 로컬 데이터를 완전히 새로고침해야 하는 경우 initialize()는 InitializeAction.LAUNCH_INITIAL_REFRESH를 반환해야 합니다. 그러면 RemoteMediator가 원격으로 새로고침을 실행하여 데이터를 완전히 새로고침합니다. APPEND 또는 PREPEND의 원격 로드는 REFRESH 로드가 성공할 때까지 기다린 후에 진행됩니다.
- 로컬 데이터를 새로고침할 필요가 없는 경우 initialize()가 InitializeAction.SKIP_INITIAL_REFRESH를 반환해야 합니다. 그러면 RemoteMediator가 원격 새로고침을 건너뛰고 캐시된 데이터를 로드합니다.
override suspend fun initialize(): InitializeAction {
val cacheTimeout = TimeUnit.MILLISECONDS.convert(1, TimeUnit.HOURS)
return if (System.currentTimeMillis() - db.lastUpdated() >= cacheTimeout)
{
// Cached data is up-to-date, so there is no need to re-fetch
// from the network.
InitializeAction.SKIP_INITIAL_REFRESH
} else {
// Need to refresh cached data from network; returning
// LAUNCH_INITIAL_REFRESH here will also block RemoteMediator's
// APPEND and PREPEND from running until REFRESH succeeds.
InitializeAction.LAUNCH_INITIAL_REFRESH
}
}
- 참조
https://developer.android.com/topic/libraries/architecture/paging/v3-network-db?hl=ko
https://www.charlezz.com/?p=44568
https://developer.android.com/codelabs/android-paging#3
'Android(Kotlin)' 카테고리의 다른 글
local.properties (0) | 2023.01.17 |
---|---|
Unix Time to SimpleDataFormat (0) | 2023.01.09 |
Room SQLCipher (0) | 2022.12.21 |
StateFlow / SharedFlow (0) | 2022.12.15 |
Hot Stream / Cold Stream (0) | 2022.12.15 |