Este documento se baseia na Visão geral da biblioteca Paging e discute como personalizar a solução de carregamento de dados do app para atender às necessidades de arquitetura dele.
Criar uma lista observável
Normalmente, o código da interface observa um objeto LiveData<PagedList> (ou, se
você estiver usando o RxJava2, um objeto Flowable<PagedList> ou
Observable<PagedList>), que reside no ViewModel do app.
Esse objeto observável forma uma conexão entre a apresentação e o conteúdo
dos dados da lista do app.
Para criar um desses objetos PagedList observáveis, transmita uma
instância de DataSource.Factory para um objeto LivePagedListBuilder ou
RxPagedListBuilder. Um objeto DataSource carrega páginas para um
único PagedList. A classe de fábrica cria novas instâncias de PagedList em
resposta a atualizações de conteúdo, como invalidações de tabelas de banco de dados e atualizações
de rede. A biblioteca de persistência do Room pode fornecer objetos DataSource.Factory
para você, ou você pode criar os seus.
O snippet de código abaixo mostra como criar uma nova instância de
LiveData<PagedList> na classe ViewModel do app usando os recursos de criação de
DataSource.Factory do Room:
ConcertDao
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>
}
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();
}
ConcertViewModel
Kotlin
// The Int type argument corresponds to a PositionalDataSource object.
val myConcertDataSource : DataSource.Factory<Int, Concert> =
concertDao.concertsByDate()
val concertList = myConcertDataSource.toLiveData(pageSize = 50)
Java
// The Integer type argument corresponds to a PositionalDataSource object.
DataSource.Factory<Integer, Concert> myConcertDataSource =
concertDao.concertsByDate();
LiveData<PagedList<Concert>> concertList =
LivePagedListBuilder(myConcertDataSource, /* page size */ 50).build();
Definir a própria configuração de paginação
Para configurar ainda mais um LiveData<PagedList> para casos avançados, você também pode
definir sua própria configuração de paginação. Em particular, é possível definir os
seguintes atributos:
- Tamanho da página: o número de itens em cada página.
- Distância da pré-busca: considerando o último item visível na IU de um app, o número de itens após o último item que a biblioteca Paging pode tentar buscar com antecedência. Esse valor precisa ser várias vezes maior que o tamanho da página.
- Presença de marcador de posição: determina se a interface exibirá marcadores de posição para os itens de lista que ainda não foram carregados. Para uma discussão sobre os benefícios e as desvantagens de usar marcadores, aprenda a fornecer marcadores na IU.
Se quiser ter mais controle sobre quando a biblioteca Paging carrega uma lista do
banco de dados do seu app, transmita um objeto Executor personalizado ao
LivePagedListBuilder, como mostrado no snippet de código a seguir:
ConcertViewModel
Kotlin
val myPagingConfig = Config(
pageSize = 50,
prefetchDistance = 150,
enablePlaceholders = true
)
// The Int type argument corresponds to a PositionalDataSource object.
val myConcertDataSource : DataSource.Factory<Int, Concert> =
concertDao.concertsByDate()
val concertList = myConcertDataSource.toLiveData(
pagingConfig = myPagingConfig,
fetchExecutor = myExecutor
)
Java
PagedList.Config myPagingConfig = new PagedList.Config.Builder()
.setPageSize(50)
.setPrefetchDistance(150)
.setEnablePlaceholders(true)
.build();
// The Integer type argument corresponds to a PositionalDataSource object.
DataSource.Factory<Integer, Concert> myConcertDataSource =
concertDao.concertsByDate();
LiveData<PagedList<Concert>> concertList =
new LivePagedListBuilder<>(myConcertDataSource, myPagingConfig)
.setFetchExecutor(myExecutor)
.build();
Escolher o tipo de fonte de dados correto
É importante se conectar à fonte de dados que processa melhor a estrutura dos seus dados de origem:
- Use
PageKeyedDataSourcese as páginas carregadas incorporarem as chaves seguinte ou anterior. Por exemplo, se você estiver buscando postagens de mídias sociais na rede, talvez precise transmitir um tokennextPagede um carregamento para o seguinte. - Use
ItemKeyedDataSourcese precisar usar dados do item N para buscar o item N+1. Por exemplo, se você estiver buscando comentários em sequência em um app de discussões, poderá ser necessário transmitir o ID do último comentário para acessar o conteúdo do comentário seguinte. - Use
PositionalDataSourcese precisar buscar páginas de dados de qualquer local escolhido no repositório de dados. Essa classe permite solicitar um conjunto de itens de dados que começa em qualquer local selecionado. Por exemplo, a solicitação pode retornar os 50 itens de dados que começam com o local 1500.
Notificar quando os dados forem inválidos
Ao usar a biblioteca Paging, a camada de dados é responsável por notificar as
outras camadas do seu app quando uma tabela ou linha se torna obsoleta. Para fazer isso, chame
invalidate() da classe DataSource que você escolheu para
o app.
Criar as próprias fontes de dados
Se você usa uma solução de dados local personalizada ou carrega dados diretamente de uma
rede, pode implementar uma das subclasses DataSource. O
snippet de código a seguir mostra uma fonte de dados que foi vinculada ao
horário de início de
determinado show.
Kotlin
class ConcertTimeDataSource() :
ItemKeyedDataSource<Date, Concert>() {
override fun getKey(item: Concert) = item.startTime
override fun loadInitial(
params: LoadInitialParams<Date>,
callback: LoadInitialCallback<Concert>) {
val items = fetchItems(params.requestedInitialKey,
params.requestedLoadSize)
callback.onResult(items)
}
override fun loadAfter(
params: LoadParams<Date>,
callback: LoadCallback<Concert>) {
val items = fetchItemsAfter(
date = params.key,
limit = params.requestedLoadSize)
callback.onResult(items)
}
}
Java
public class ConcertTimeDataSource
extends ItemKeyedDataSource<Date, Concert> {
@NonNull
@Override
public Date getKey(@NonNull Concert item) {
return item.getStartTime();
}
@Override
public void loadInitial(@NonNull LoadInitialParams<Date> params,
@NonNull LoadInitialCallback<Concert> callback) {
List<Concert> items =
fetchItems(params.key, params.requestedLoadSize);
callback.onResult(items);
}
@Override
public void loadAfter(@NonNull LoadParams<Date> params,
@NonNull LoadCallback<Concert> callback) {
List<Concert> items =
fetchItemsAfter(params.key, params.requestedLoadSize);
callback.onResult(items);
}
}
Em seguida, é possível carregar esses dados personalizados em objetos PagedList criando uma
subclasse concreta de DataSource.Factory. O snippet de código abaixo mostra
como gerar novas instâncias da fonte de dados personalizada definida no snippet de código
anterior:
Kotlin
class ConcertTimeDataSourceFactory :
DataSource.Factory<Date, Concert>() {
val sourceLiveData = MutableLiveData<ConcertTimeDataSource>()
var latestSource: ConcertDataSource?
override fun create(): DataSource<Date, Concert> {
latestSource = ConcertTimeDataSource()
sourceLiveData.postValue(latestSource)
return latestSource
}
}
Java
public class ConcertTimeDataSourceFactory
extends DataSource.Factory<Date, Concert> {
private MutableLiveData<ConcertTimeDataSource> sourceLiveData =
new MutableLiveData<>();
private ConcertDataSource latestSource;
@Override
public DataSource<Date, Concert> create() {
latestSource = new ConcertTimeDataSource();
sourceLiveData.postValue(latestSource);
return latestSource;
}
}
Considerar como as atualizações de conteúdo funcionam
Ao criar objetos PagedList observáveis, considere como as atualizações de conteúdo
funcionam. Se os dados forem carregados diretamente de um banco de dados do Room,
as atualizações serão enviadas automaticamente para a IU do seu app.
Ao usar uma API de rede paginada, você normalmente tem uma interação do usuário, como
"deslizar para atualizar", que serve como um sinal para invalidar o DataSource
usado mais recentemente. Em seguida, você solicita uma nova instância dessa fonte de dados. O snippet de código abaixo demonstra esse comportamento:
Kotlin
class ConcertActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// ...
concertTimeViewModel.refreshState.observe(this, Observer {
// Shows one possible way of triggering a refresh operation.
swipeRefreshLayout.isRefreshing =
it == MyNetworkState.LOADING
})
swipeRefreshLayout.setOnRefreshListener {
concertTimeViewModel.invalidateDataSource()
}
}
}
class ConcertTimeViewModel(firstConcertStartTime: Date) : ViewModel() {
val dataSourceFactory = ConcertTimeDataSourceFactory(firstConcertStartTime)
val concertList: LiveData<PagedList<Concert>> =
dataSourceFactory.toLiveData(
pageSize = 50,
fetchExecutor = myExecutor
)
fun invalidateDataSource() =
dataSourceFactory.sourceLiveData.value?.invalidate()
}
Java
public class ConcertActivity extends AppCompatActivity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
// ...
viewModel.getRefreshState()
.observe(this, new Observer<NetworkState>() {
// Shows one possible way of triggering a refresh operation.
@Override
public void onChanged(@Nullable MyNetworkState networkState) {
swipeRefreshLayout.isRefreshing =
networkState == MyNetworkState.LOADING;
}
};
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshListener() {
@Override
public void onRefresh() {
viewModel.invalidateDataSource();
}
});
}
}
public class ConcertTimeViewModel extends ViewModel {
private LiveData<PagedList<Concert>> concertList;
private DataSource<Date, Concert> mostRecentDataSource;
public ConcertTimeViewModel(Date firstConcertStartTime) {
ConcertTimeDataSourceFactory dataSourceFactory =
new ConcertTimeDataSourceFactory(firstConcertStartTime);
mostRecentDataSource = dataSourceFactory.create();
concertList = new LivePagedListBuilder<>(dataSourceFactory, 50)
.setFetchExecutor(myExecutor)
.build();
}
public void invalidateDataSource() {
mostRecentDataSource.invalidate();
}
}
Fornecer mapeamento de dados
A biblioteca Paging é compatível com transformações baseadas em itens e em páginas de itens
carregados por uma DataSource.
No snippet de código a seguir, uma combinação do nome e da data do show é mapeada para uma única string contendo o nome e a data.
Kotlin
class ConcertViewModel : ViewModel() {
val concertDescriptions : LiveData<PagedList<String>>
init {
val concerts = database.allConcertsFactory()
.map { "${it.name} - ${it.date}" }
.toLiveData(pageSize = 50)
}
}
Java
public class ConcertViewModel extends ViewModel {
private LiveData<PagedList<String>> concertDescriptions;
public ConcertViewModel(MyDatabase database) {
DataSource.Factory<Integer, Concert> factory =
database.allConcertsFactory().map(concert ->
concert.getName() + "-" + concert.getDate());
concertDescriptions = new LivePagedListBuilder<>(
factory, /* page size */ 50).build();
}
}
Isso poderá ser útil se você quiser unir, converter ou preparar itens depois que eles forem carregados. Como esse trabalho é feito no executor de busca, é possível realizar tarefas potencialmente caras, como ler o disco ou consultar um banco de dados separado.
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
- Visão geral da Biblioteca Paging 2
- Exibir listas paginadas