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

Paging3 데이터 로드 본문

Android Jetpack Architecture/Paging3

Paging3 데이터 로드

hik14 2021. 8. 19. 15:07

 PagingSource 정의 하기

1. 데이터 소스를 식별

 

- PagingSource<key, value> 

- key는 데이터 로드에 사용하는 식별자(index) value는 로드되는 데이터의 타입.

 

 2. load() 데이터 소스에서 페이징된 데이터를 검색하는 방법을 정의하는 메소드

 

- PagingSource는 서버에서 데이터를 가져올수 있는 backend, 서버에 전송할 인자 query를 생성자 매개변수로 받는다. 

 

-  첫 번째 호출은 Pager 에 의해 제공되는 initialKey 를 사용하여 load한다.

 

LoadParams 객체에는 실행할 로드 작업에 관한 정보가 포함됩니다.  로드할 키와 로드할 항목 개수가 포함되어 있다. 

LoadResult 객체에는 로드 작업의 결과가 포함됩니다. LoadResult는 load() 호출이 성공했는지에 따라 두 가지 형식 중 하나를 취하는 Seald Class 입니다.

  • 로드에 성공하면 LoadResult.Page 객체를 반환합니다.
  • 로드에 실패하면 LoadResult.Error 객체를 반환합니다.
class ExamplePagingSource(
    val backend: ExampleBackendService,
    val query: String
) : PagingSource<Int, User>() {

  override suspend fun load(
    params: LoadParams<Int>
  ): LoadResult<Int, User> {
     .
     .
     .
  }

  override fun getRefreshKey(state: PagingState<Int, User>): Int? {
    .
    .
    .
  }
}

3.  getRefreshKey()

override fun getRefreshKey(state: PagingState<Int, User>): Int? {
    
    // he prevKey 또는 the nextKey 중에서
    // anchorPosition에 가장 근접한 페이지 key를 찾는다
    // null 처리는 직접 해줘야한다.
   
    //  * prevKey == null -> anchorPage 처음 페이지
    //  * nextKey == null -> anchorPage 마지막 페이지
    //  * both prevKey and nextKey null -> anchorPage 초기화, 그냥 null 반환
    
    return state.anchorPosition?.let { anchorPosition ->
      val anchorPage = state.closestPageToPosition(anchorPosition)
      anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
    }
  }

-  load()의 후속 새로고침 호출에 사용

 

-  PagingState 객체를 이용해서 첫 로드후 새로고침/무효화 되면 key를 반환하여 load()함수로 전달한다. 

 

- 스와이프하여 새로고침 / 데이터베이스 업데이트 / 구성 변경 /프로세스 중단 등으로 인해 무효화될때,

Paging 라이브러리가 현재 목록을 대체할 새 데이터를 로드하려고 할 때마다 호출

 

- 일반적으로 후속 새로고침 호출은 가장 최근에 액세스한 인덱스를 나타내는 PagingState.anchorPosition 주변 데이터의 로드를 다시 시작하려고 합니다.  

 

4. 오류 처리하기. 

 

데이터로드중 여러가지 이유로 실패할 수 있다. 

로드하는 중에 load() 메서드에서 LoadResult.Error 객체를 반환을 통하여 오류를 처리한다.

 override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Repo> {

        val position = params.key ?: GITHUB_STARTING_PAGE_INDEX
        val apiQuery = query + IN_QUALIFIER

        return try {
            val response = service.searchRepos(apiQuery, position, params.loadSize)
            val repos = response.items

            val nextKey =
                if (repos.isEmpty()) {
                    null
                }
                else {
                    // 초기 로드 크기 3 * NETWORK_PAGE_SIZE
                    // 두번쨰 요청에서 중복된 항목을 요청하지 않는지 확인한다.
                    position + (params.loadSize / NETWORK_PAGE_SIZE)
                }

            LoadResult.Page(
                data = repos,
                prevKey = if (position == GITHUB_STARTING_PAGE_INDEX) null else position - 1,
                nextKey = nextKey
            )

        } catch (exception: IOException) {
           // 네트워크 연결 자체를 실패한 경우 처리
            return LoadResult.Error(exception)
        } catch (exception:HttpException){
           // http 통신중 http 오류코드(400, 403..)를 통한 처리
            return LoadResult.Error(exception)
        }
    }