O que é a paging library?
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 um usuário chegar ao final do RecylerView. Nessa abordagem, era difícil manter a eficiência e a lógica da interface do usuário. Além disso, gera uma lógica de dados complicada, o que dificulta a depuração e o teste do projeto.
A paging library é uma biblioteca criada para resolver o problema da paginação de dados no Android.
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 tentar 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 na versão 3.6 ou superior. Até o momento da escrita deste artigo, esta é a versão atual da biblioteca
def paging_version = "3.0.0-alpha07" | |
implementation "androidx.paging: paging-runtime:$paging_version" |
Fluxo dos dados
O principal componente na camada de Repository é o PagingSource.
Cada objeto PagingSource define uma fonte de dados e como recuperar os dados dessa fonte, seja ela um banco de dados ou uma API externa. No nosso exemplo utilizarei uma API externa desse link.
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 request através do PagingConfig e nos auxiliar no fluxo de dados.
A partir do 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 iremos 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 :
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
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 | |
} | |
} |
Todo o conteúdo foi compartilhado com um projeto exemplo está nesse link!
Links adicionais :
- Artigo “Como usar o Jetpack Navigation em projetos multi módulos”
- https://developer.android.com/topic/libraries/architecture/paging/v3-overview
- https://codelabs.developers.google.com/codelabs/android-paging/#0
- https://gist.github.com/1jGabriel/09564a5d1ca73e76ea04091c3e5b5809