Utilizando o Jetpack Paging v3
Tags: android, kotlin, jetpack, paging
Published: 2020-07-16 20:02:35

## O que é a Paging Library?

A **Paging Library**, é uma biblioteca criada pelo **Google** para resolver o problema da paginação de dados no **Android**. 

Um problema que geralmente temos que resolver ao criar um aplicativo, é a solicitação de dados longos, que não exigem uma única vez, pois o usuário vê apenas uma pequena parte desses dados por vez.

Uma maneira de resolver esse problema, é adicionar um *ScrollListener* para o *RecyclerView* e carregar mais dados quando o usuário chega ao final da lista. Com esta abordagem, é difícil manter a eficiência, também a lógica da interface do usuário e gera uma lógica de dados complicada, o que dificulta a depuração e testes do projeto.

## Vantagens

- A **Paging Library** ajuda a carregar e exibir pequenos blocos de dados por vez. 

- O carregamento de dados parciais sob demanda reduz o uso da largura de banda da rede e dos recursos do sistema.

- Facilmente integrada ao *RecyclerView*.

- Mantém o controle das chaves a serem usadas para recuperar a página seguinte e a anterior.
- Solicita automaticamente a página correta quando o usuário rola para o final da lista. 
- Garante que várias solicitações não sejam acionadas ao mesmo tempo.
- Rastreia o estado de carregamento e permite exibi-lo em um *RecyclerView* item da lista, ou em qualquer outro local da interface do usuário e mostrar facilmente as cargas com falha.

## Novidades Paging 3

- Suporte interno para tratamento de erros, incluindo mecanismo de reload e retry.
- Suporte à *coroutines* e *Flow* do *Kotlin*, assim como era ao *LiveData* e *RxJava* na versão 2.
- *Header*, *footer* e *separator* incluídos na biblioteca.

## Como implementar

Primeiramente, é necessário ter o [Android Studio](https://developer.android.com/studio) na versão 3.6 ou superior.

Adicionar a dependência no *build.gradle*
```groovy
def paging_version = "3.0.0-alpha02"
implementation "androidx.paging: paging-runtime: $ paging_version"
```

> Até o momento da escrita deste artigo, esta é a versão atual da biblioteca

### Fluxo dos dados

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/utdrgs1ce485ib8p4jic.png)

O principal componente na camada de *Repository* é o *PagingSource*.
Cada objeto *PagingSource*, define uma fonte de dados e recupera os dados dessa fonte, seja ela um banco de dados local ou uma API externa. No nosso exemplo utilizarei uma API externa: 

```kotlin
class MyPagingSource(
    private val api: MyApi
) : PagingSource<Int, MyModel>() {
    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, MyModel> {
        return try {
            val result = api.getItems(params.key ?: STARTING_PAGE_INDEX)
            Page(
                data = result,
                prevKey = params.key,
                nextKey = result.nextPage ?: STARTING_PAGE_INDEX.plus(1)
            )
        } catch (e: IOException) {
            LoadResult.Error(e)
        } catch (e: HttpException) {
            LoadResult.Error(e)
        }
    }

    companion object {
        private const val STARTING_PAGE_INDEX = 1
    }
}
```

A partir da criação do nosso *PagingSource* na camada de *Repository*, criaremos o nosso *Pager*.

O *Pager* é o responsável por configurar o tamanho de nossas páginas de requisição, através do *PagingConfig*, e auxiliar no fluxo dos dados.

``` kotlin
fun getItems(): Flow<PagingData<MyModel>> {
    return Pager(
          config = PagingConfig(pageSize = 20),
          pagingSourceFactory = { MyPagingSource(api = api) }
    ).flow.cachedIn(viewModelScope)
}
```

> A partir da Paging v3, não é necessário converter *Flow* em *LiveData*.
> Para manter um cache dos dados carregados no nosso *ViewModel*, chamamos o *cachedIn* passando o nosso *androidx.lifecycle.viewModelScope*

Agora teremos a nossa última etapa de implementação, que é  passar os dados carregados para a nossa *View*.

Será necessário coletar os dados vindos do *PagingData* e passar para o nosso *Adapter*:

- Solicitando os dados para o *viewModel*:

```kotlin
fun getData() {
    lifecycleScope.launch {
    	viewModel.getItems().collectLatest {
        	adapter.submitData(it)
    	}
     }
}
```

- A implementação do *adapter* só precisa fazer uma extensão da classe PagingDataAdapter, e passar um *DiffUtilCallback* para diferenciar os itens recebidos:

```kotlin
class MyAdapter : PagingDataAdapter<MyModel, MyAdapter.MyViewHolder>(DiffUtilCallback()) {
   // a implementação do corpo do adapter permanece a mesma
}

class DiffUtilCallBack : DiffUtil.ItemCallback<MyModel>() {
    override fun areItemsTheSame(oldItem: MyModel, newItem: MyModel): Boolean {
        return oldItem.id == newItem.id
    }

    override fun areContentsTheSame(oldItem: MyModel, newItem: MyModel): Boolean {
        return oldItem == newItem
    }
}
```
-----------------------------
Esta foi a implementação necessária para fazer paginação no **Android** usando a *Paging Library*, obrigado pela leitura

Todo o conteúdo foi compartilhado no meu [Github](
https://github.com/1jGabriel/paging-v3-sample)

Vou deixar aqui links adicionais com a documentação da biblioteca:

https://developer.android.com/topic/libraries/architecture/paging/v3-overview

https://codelabs.developers.google.com/codelabs/android-paging/#0