Visão geral da biblioteca Paging 2 Parte do Android Jetpack.
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.
Este guia oferece vários exemplos conceituais da biblioteca, além de uma visão geral de como ela funciona. Para ver exemplos completos de como essa biblioteca funciona, consulte o codelab e os exemplos da seção Outros recursos.
Configurar
Para importar componentes da biblioteca Paging para seu app Android, adicione as seguintes
dependências ao arquivo build.gradle do app:
Groovy
dependencies { def paging_version = "2.1.2" implementation "androidx.paging:paging-runtime:$paging_version" // For Kotlin use paging-runtime-ktx // alternatively - without Android dependencies for testing testImplementation "androidx.paging:paging-common:$paging_version" // For Kotlin use paging-common-ktx // optional - RxJava support implementation "androidx.paging:paging-rxjava2:$paging_version" // For Kotlin use paging-rxjava2-ktx }
Kotlin
dependencies { val paging_version = "2.1.2" implementation("androidx.paging:paging-runtime:$paging_version") // For Kotlin use paging-runtime-ktx // alternatively - without Android dependencies for testing testImplementation("androidx.paging:paging-common:$paging_version") // For Kotlin use paging-common-ktx // optional - RxJava support implementation("androidx.paging:paging-rxjava2:$paging_version") // For Kotlin use paging-rxjava2-ktx }
Arquitetura da biblioteca
Esta seção descreve e mostra os principais componentes da Paging Library.
PagedList
O componente principal da biblioteca Paging é a classe PagedList, que carrega
partes dos dados do app, ou páginas. À medida que mais dados são necessários, eles são paginados no
objeto PagedList atual. Se algum dado carregado mudar, uma nova instância de
PagedList será emitida para o detentor de dados observáveis de um objeto baseado em LiveData ou
RxJava2. À medida que os objetos PagedList são gerados, a interface do app
apresenta o conteúdo deles, respeitando os
ciclos de vida dos controladores de interface.
O snippet de código a seguir mostra como é possível configurar o modelo de visualização de seu app para
carregar e apresentar dados usando um armazenador LiveData de objetos PagedList.
Kotlin
class ConcertViewModel(concertDao: ConcertDao) : ViewModel() {
val concertList: LiveData<PagedList<Concert>> =
concertDao.concertsByDate().toLiveData(pageSize = 50)
}
Java
public class ConcertViewModel extends ViewModel {
private ConcertDao concertDao;
public final LiveData<PagedList<Concert>> concertList;
// Creates a PagedList object with 50 items per page.
public ConcertViewModel(ConcertDao concertDao) {
this.concertDao = concertDao;
concertList = new LivePagedListBuilder<>(
concertDao.concertsByDate(), 50).build();
}
}
Dados
Cada instância de PagedList carrega um snapshot atualizado dos dados do app
do objeto DataSource correspondente. Os dados fluem do back-end ou do banco de dados do app para o objeto PagedList.
O exemplo a seguir usa a biblioteca de persistência do Room para organizar os dados do app, mas, se você quiser armazenar os dados usando outro meio, também poderá fornecer sua própria fábrica de fontes de dados.
Kotlin
@Dao
interface ConcertDao {
// The Int type parameter tells Room to use a PositionalDataSource object.
@Query("SELECT * FROM concerts ORDER BY date DESC")
fun concertsByDate(): DataSource.Factory<Int, Concert>
}
Java
@Dao
public interface ConcertDao {
// The Integer type parameter tells Room to use a
// PositionalDataSource object.
@Query("SELECT * FROM concerts ORDER BY date DESC")
DataSource.Factory<Integer, Concert> concertsByDate();
}
Para saber mais sobre como carregar dados em objetos PagedList, consulte o
guia sobre como Carregar dados paginados.
Interface
A classe PagedList funciona com um PagedListAdapter para carregar itens em um
RecyclerView. Essas classes trabalham juntas para buscar e exibir conteúdo
à medida que ele é carregado, pré-buscando conteúdo fora de visualização e animando mudanças de conteúdo.
Para saber mais, consulte o guia sobre como Exibir listas paginadas.
Oferece suporte a diferentes arquiteturas de dados
A Paging Library é compatível com as arquiteturas de dados a seguir:
- Veiculada somente de um servidor de back-end.
- Armazenada somente em um banco de dados do dispositivo.
- Uma combinação das outras origens, usando o banco de dados do dispositivo como cache.
A figura 1 mostra como é o fluxo de dados cada um desses cenários de arquitetura. No caso de uma solução somente de rede ou de banco de dados, os dados fluem diretamente para o modelo de IU do seu app. Caso você esteja usando uma abordagem combinada, os dados fluem do servidor de back-end para um banco de dados do dispositivo e depois para o modelo de IU do app. De vez em quando, o endpoint de cada fluxo fica sem dados para carregar e solicita mais dados do componente que os forneceu. Por exemplo, quando um banco de dados do dispositivo fica sem dados, ele solicita mais do servidor.
O restante desta seção oferece recomendações para a configuração de cada caso de uso de fluxo de dados.
Somente rede
Para exibir dados de um servidor de back-end, use a versão síncrona da
API Retrofit para carregar informações no seu próprio objeto
DataSource personalizado.
Somente banco de dados
Configure o RecyclerView para observar o armazenamento local, de preferência usando a
biblioteca de persistência do Room. Dessa forma, sempre que dados forem inseridos ou modificados
no banco de dados do app, essas mudanças serão refletidas automaticamente no
RecyclerView que está mostrando esses dados.
Rede e banco de dados
Depois de começar a observar o banco de dados, será possível detectar quando
ele estiver sem dados usando PagedList.BoundaryCallback. Você pode
buscar mais itens da sua rede e inseri-los no banco de dados. Caso
a IU esteja observando o banco de dados, é só isso que você precisa fazer.
Processar erros de rede
Ao usar uma rede para buscar ou paginar os dados exibidos usando a biblioteca Paging, é importante não tratar a rede como "disponível" "ou" "indisponível" o tempo todo, porque muitas conexões são intermitentes ou lentas:
- Um servidor específico pode não responder a uma solicitação de rede.
- O dispositivo pode estar conectado a uma rede lenta ou fraca.
Em vez disso, seu app precisa verificar se há falhas em cada solicitação e recuperá-las da melhor forma possível nos casos em que a rede não está disponível. Por exemplo, você pode oferecer um botão "Tentar novamente" a ser selecionado pelos usuários quando a etapa de atualização de dados não funcionar. Caso ocorra um erro durante a etapa de paginação de dados, é melhor repetir as solicitações de paginação de forma automática.
Atualizar seu app existente
Caso seu app já consuma dados de um banco de dados ou de uma fonte de back-end, é possível fazer upgrade diretamente para a função oferecida pela biblioteca Paging. Esta seção mostra como fazer upgrade de um app com design comum.
Soluções personalizadas de paginação
Se você usa uma funcionalidade personalizada para carregar pequenos subconjuntos de dados da fonte de dados do seu app, pode substituir essa lógica pela da classe PagedList. As instâncias do PagedList oferecem conexões integradas com fontes de dados
comuns. Essas instâncias também fornecem adaptadores para objetos RecyclerView
que podem ser incluídos na interface do app.
Dados carregados usando listas, em vez de páginas
Se você usar uma lista na memória como a estrutura de dados de apoio para o adaptador da
interface, considere observar as atualizações de dados usando uma classe PagedList se o
número de itens na lista puder se tornar grande. As instâncias de PagedList podem usar
LiveData<PagedList> ou Observable<List> para transmitir atualizações de dados para
a interface do app, minimizando os tempos de carregamento e o uso da memória. Melhor ainda, substituir um objeto
List por um objeto PagedList no app não exige
mudanças na estrutura da interface ou na lógica de atualização de dados.
Associar um cursor de dados a uma visualização em lista usando o CursorAdapter
Seu app pode usar um CursorAdapter para associar dados de um
Cursor a um ListView. Nesse caso, você geralmente precisa
migrar de uma ListView para uma RecyclerView como o contêiner de IU da lista do seu app
e depois substituir o componente Cursor por Room ou
PositionalDataSource, caso as instâncias de Cursor acessem um
banco de dados SQLite.
Em algumas situações, como ao trabalhar com instâncias de Spinner, você
fornece apenas o adaptador. Em seguida, uma biblioteca pega os dados carregados
nesse adaptador e os exibe para você. Nessas situações, mude o
tipo de dados do adaptador para LiveData<PagedList> e, em seguida, envolva essa
lista em um objeto ArrayAdapter antes de tentar fazer com que uma classe de biblioteca
infle esses itens em uma interface.
Carregar conteúdo de forma assíncrona usando o AsyncListUtil
Se você estiver usando objetos AsyncListUtil para carregar e exibir grupos de
informações de forma assíncrona, a biblioteca Paging simplifica o carregamento de dados:
- Seus dados não precisam ser de posicionamento. A biblioteca Paging permite carregar dados diretamente do seu back-end usando as chaves fornecidas pela rede.
- Seus dados podem ser extremamente grandes. Usando a biblioteca Paging, é possível carregar dados em páginas até que não haja mais dados restantes.
- Você pode observar seus dados com mais facilidade. A biblioteca Paging pode apresentar os dados que o ViewModel do seu app mantém em uma estrutura de dados observável.
Exemplos de banco de dados
Os snippets de código a seguir mostram várias maneiras possíveis de fazer com que todas as partes funcionem juntas.
Observar dados paginados usando o LiveData
O snippet de código a seguir mostra todas as partes funcionando juntas. À medida que os eventos de show são adicionados, removidos ou alterados no banco de dados, o conteúdo na RecyclerView é atualizado de forma automática e eficiente:
Kotlin
@Dao
interface ConcertDao {
// The Int type parameter tells Room to use a PositionalDataSource
// object, with position-based loading under the hood.
@Query("SELECT * FROM concerts ORDER BY date DESC")
fun concertsByDate(): DataSource.Factory<Int, Concert>
}
class ConcertViewModel(concertDao: ConcertDao) : ViewModel() {
val concertList: LiveData<PagedList<Concert>> =
concertDao.concertsByDate().toLiveData(pageSize = 50)
}
class ConcertActivity : AppCompatActivity() {
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Use the 'by viewModels()' Kotlin property delegate
// from the activity-ktx artifact
val viewModel: ConcertViewModel by viewModels()
val recyclerView = findViewById(R.id.concert_list)
val adapter = ConcertAdapter()
viewModel.concertList.observe(this, PagedList(adapter::submitList))
recyclerView.setAdapter(adapter)
}
}
class ConcertAdapter() :
PagedListAdapter<Concert, ConcertViewHolder>(DIFF_CALLBACK) {
fun onBindViewHolder(holder: ConcertViewHolder, position: Int) {
val concert: Concert? = getItem(position)
// Note that "concert" is a placeholder if it's null.
holder.bindTo(concert)
}
companion object {
private val DIFF_CALLBACK = object :
DiffUtil.ItemCallback<Concert>() {
// Concert details may have changed if reloaded from the database,
// but ID is fixed.
override fun areItemsTheSame(oldConcert: Concert,
newConcert: Concert) = oldConcert.id == newConcert.id
override fun areContentsTheSame(oldConcert: Concert,
newConcert: Concert) = oldConcert == newConcert
}
}
}
Java
@Dao
public interface ConcertDao {
// The Integer type parameter tells Room to use a PositionalDataSource
// object, with position-based loading under the hood.
@Query("SELECT * FROM concerts ORDER BY date DESC")
DataSource.Factory<Integer, Concert> concertsByDate();
}
public class ConcertViewModel extends ViewModel {
private ConcertDao concertDao;
public final LiveData<PagedList<Concert>> concertList;
public ConcertViewModel(ConcertDao concertDao) {
this.concertDao = concertDao;
concertList = new LivePagedListBuilder<>(
concertDao.concertsByDate(), /* page size */ 50).build();
}
}
public class ConcertActivity extends AppCompatActivity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ConcertViewModel viewModel =
new ViewModelProvider(this).get(ConcertViewModel.class);
RecyclerView recyclerView = findViewById(R.id.concert_list);
ConcertAdapter adapter = new ConcertAdapter();
viewModel.concertList.observe(this, adapter::submitList);
recyclerView.setAdapter(adapter);
}
}
public class ConcertAdapter
extends PagedListAdapter<Concert, ConcertViewHolder> {
protected ConcertAdapter() {
super(DIFF_CALLBACK);
}
@Override
public void onBindViewHolder(@NonNull ConcertViewHolder holder,
int position) {
Concert concert = getItem(position);
if (concert != null) {
holder.bindTo(concert);
} else {
// Null defines a placeholder item - PagedListAdapter automatically
// invalidates this row when the actual object is loaded from the
// database.
holder.clear();
}
}
private static DiffUtil.ItemCallback<Concert> DIFF_CALLBACK =
new DiffUtil.ItemCallback<Concert>() {
// Concert details may have changed if reloaded from the database,
// but ID is fixed.
@Override
public boolean areItemsTheSame(Concert oldConcert, Concert newConcert) {
return oldConcert.getId() == newConcert.getId();
}
@Override
public boolean areContentsTheSame(Concert oldConcert,
Concert newConcert) {
return oldConcert.equals(newConcert);
}
};
}
Observar dados paginados usando o RxJava2
Se preferir usar RxJava2 em vez de LiveData, você pode
criar um objeto Observable ou Flowable:
Kotlin
class ConcertViewModel(concertDao: ConcertDao) : ViewModel() {
val concertList: Observable<PagedList<Concert>> =
concertDao.concertsByDate().toObservable(pageSize = 50)
}
Java
public class ConcertViewModel extends ViewModel {
private ConcertDao concertDao;
public final Observable<PagedList<Concert>> concertList;
public ConcertViewModel(ConcertDao concertDao) {
this.concertDao = concertDao;
concertList = new RxPagedListBuilder<>(
concertDao.concertsByDate(), /* page size */ 50)
.buildObservable();
}
}
Em seguida, você pode iniciar e parar a observação dos dados usando o código no snippet a seguir:
Kotlin
class ConcertActivity : AppCompatActivity() {
private val adapter = ConcertAdapter()
// Use the 'by viewModels()' Kotlin property delegate
// from the activity-ktx artifact
private val viewModel: ConcertViewModel by viewModels()
private val disposable = CompositeDisposable()
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val recyclerView = findViewById(R.id.concert_list)
recyclerView.setAdapter(adapter)
}
override fun onStart() {
super.onStart()
disposable.add(viewModel.concertList
.subscribe(adapter::submitList)))
}
override fun onStop() {
super.onStop()
disposable.clear()
}
}
Java
public class ConcertActivity extends AppCompatActivity {
private ConcertAdapter adapter = new ConcertAdapter();
private ConcertViewModel viewModel;
private CompositeDisposable disposable = new CompositeDisposable();
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
RecyclerView recyclerView = findViewById(R.id.concert_list);
viewModel = new ViewModelProvider(this).get(ConcertViewModel.class);
recyclerView.setAdapter(adapter);
}
@Override
protected void onStart() {
super.onStart();
disposable.add(viewModel.concertList
.subscribe(adapter.submitList(flowableList)
));
}
@Override
protected void onStop() {
super.onStop();
disposable.clear();
}
}
O código para ConcertDao e ConcertAdapter é o mesmo para uma
solução baseada em RxJava2 e para uma
solução baseada em LiveData.
Enviar feedback
Envie comentários e ideias usando os recursos abaixo:
- Issue tracker

- Informe os problemas para que possamos corrigir os bugs.
Outros recursos
Para saber mais sobre a biblioteca Paging, consulte os recursos a seguir.
Amostras
- Amostra de paginação dos Componentes da arquitetura do Android (link em inglês)
- Exemplo de paginação com rede (link em inglês)
Codelabs
Vídeos
- Android Jetpack: gerenciar listas infinitas com RecyclerView e Paging (Google I/O '18)
- Android Jetpack: Paging
Recomendados para você
- Observação: o texto do link aparece quando o JavaScript está desativado.
- Migrar para a Paging 3
- Exibir listas paginadas
- Coletar dados paginados