오늘도 더 나은 코드를 작성하였습니까?

RemoteMediator 구현하기 본문

Android Jetpack Architecture/Paging3

RemoteMediator 구현하기

hik14 2021. 8. 19. 19:55

RemoteMediator의 기본 구현

 

Pager가 데이터를 모두 사용했거나 기존 데이터가 무효화되었을 때 네트워크에서 더 많은 데이터를 로드하는 것입니다. RemoteMediator는 로드 동작을 정의하기 위해 재정의해야 하는 load() 메서드를 포함합니다.

 

일반적인 RemoteMediator 구현에는 다음 매개변수가 포함됩니다.

  • query  백엔드에서 검색할 데이터를 정의하는 쿼리 문자열
  • database: 로컬 캐시의 역할을 하는 데이터베이스
  • networkService: 백엔드 서비스 API 요청  

RemoteMediator<Key, Value> 구현을 만듭니다.

 Key 유형과 Value 유형은 동일한 네트워크 데이터 소스의 PagingSource를 정의하는 경우와 동일하다.

@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() 메서드는 데이터 세트를 업데이트하고 기존 PagingSource를 무효화합니다

 페이징을 지원하는 일부 라이브러리(예: Room)는 구현하는 PagingSource 객체의 무효화를 자동으로 처리합니다.

 

load() 메서드는 다음과 같은 두 매개변수를 사용합니다.

 

매개변수

 

PagingState: 지금까지 로드한 페이지, 가장 최근에 액세스한 색인, 페이징 스트림을 초기화하는 데 사용한 PagingConfig 객체에 관한 정보를 포함합니다.

 

LoadType: 로드의 유형을 REFRESH,  APPEND,  PREPEND 중 하나로 나타냅니다.

이전에 로드한 데이터의 끝 부분(LoadType.APPEND) / 시작 부분(LoadType.PREPEND) / 데이터를 처음으로 로드하는지(LoadType.REFRESH)

 

반환값

 

 MediatorResult :  MediatorResult.Error(오류 설명 포함) 또는 MediatorResult.Success(로드할 데이터가 더 있는지 여부를 나타내는 신호 포함)일 수 있습니다.

 

load()의 기본 로직.

 

- 로드 유형(LoadType)및 지금까지 로드된 데이터에 따라 네트워크에서 로드할 페이지를 결정합니다

- 네트워크 요청을 트리거합니다

- 로드 작업 결과에 따라 작업을 실행합니다

 

성공적인 데이터 로드

 

 - 로드한 데이터가 비어 있지 않으면 데이터베이스에 저장해줌

 - MediatorResult.Success(endOfPaginationReached = false) 반환하여 아직 마지막 페이지가 아님을 알린다. 

 -  데이터가 저장된 후에는 PagingSource를 무효화하여 페이징 라이브러리에 새 데이터를 알립니다.

 

 - 로드가 성공했지만 수신된 항목 비어 있거나 마지막 페이지인 경우 MediatorResult.Success(endOfPaginationReached = true)를 반환합니다. 이전 페이지가 마지막이였음을 알려줌. 

 - 데이터가 저장된 후에는 PagingSource를 무효화하여 페이징 라이브러리에 새 데이터를 알립니다.

 

오류가 발생한 경우 

 

- MediatorResult.Error를 반환합니다.

 

초기화 메소드 initialze() 정의하기

 initialize() 

 

- 초기화 담당. 

- 메서드를 재정의하여 캐시된 데이터가 오래되었는지 점검하고 원격 새로고침을 트리거할지 결정 하는 곳.

- 로드하기 전에 실행되므로 로컬 또는 원격 로드를 트리거하기 전에 데이터베이스를 조작할 수 있습니다(예: 기존 데이터 삭제)

- InitializeAction을 반환

 

initialize()는 비동기 함수이므로 데이터를 로드하여 데이터베이스에 있는 기존 데이터의 관련성을 확인할 수 있습니다.

가장 일반적인 사례는 캐시된 데이터가 특정 기간에만 유효한 경우입니다.

 RemoteMediator는 만료 기간이 지났는지 확인할 수 있으며, 이런 경우 페이징 라이브러리는 데이터를 완전히 REFRESH해야 합니다. 

override suspend fun initialize(): InitializeAction {
  val cacheTimeout = TimeUnit.HOURS.convert(1, TimeUnit.MILLISECONDS)
  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
  }
}

 

 

Pager 생성

- PagingSource 생성자를 직접 전달하는 대신 DAO에서 PagingSource 객체를 반환하는 쿼리 메서드를 제공(pagingsourceFactory)

- RemoteMediator 객체를 remoteMediator 매개변수로 제공

    @OptIn(androidx.paging.ExperimentalPagingApi::class)
    fun getSearchResultStream(query: String): Flow<PagingData<Repo>> {

        val dbQuery = "%${query.replace(' ', '%')}%"

        val pagingSourceFactory =  { database.reposDao().reposByName(dbQuery) }

        return Pager(
            config = PagingConfig(
                pageSize = NETWORK_PAGE_SIZE,
                enablePlaceholders = false
            ),
            remoteMediator = GithubRemoteMediator(
                query,
                service,
                database
            ),
            pagingSourceFactory = pagingSourceFactory
        ).flow
    }