Este guia foi desenvolvido com base 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 IU observa um objeto
LiveData<PagedList>
(ou,
se você estiver usando 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 de
lista do seu 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 uma única 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
Room pode fornecer objetos DataSource.Factory
para você, ou você pode criar objetos próprios.
O snippet de código a seguir mostra como criar uma nova instância de
LiveData<PagedList>
na classe
ViewModel
do seu app
com o recursos de construção
DataSource.Factory
da Room.
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(); }
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
LiveData<PagedList>
para casos avançados,
também é possível definir sua própria configuração de paginação. Você pode definir
especialmente 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: determina se a IU exibirá marcadores para os itens de listas que ainda não foram carregados. Para ver uma discussão sobre os benefícios e as desvantagens de usar marcadores, consulte 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.
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
PageKeyedDataSource
se as páginas carregadas incorporam chaves anteriores ou subsequentes. Por exemplo, caso você esteja buscando postagens de mídia social na rede, talvez seja necessário transmitir um tokennextPage
de um carregamento para um carregamento subsequente. - Use
ItemKeyedDataSource
se 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
PositionalDataSource
se precisar buscar páginas de dados de qualquer local escolhido no seu armazenamento de dados. Essa classe é compatível com a solicitação de um conjunto de itens de dados a partir de qualquer local selecionado. Por exemplo, a solicitação pode retornar os 50 itens de dados do 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 isso, chame
invalidate()
da classe de
DataSource
que você
escolheu para seu 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, é possível implementar uma das subclasses de
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, carregue esses dados personalizados em objetos PagedList
criando uma
subclasse concreta de
DataSource.Factory
. O
snippet de código a seguir 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 construir objetos observáveis
PagedList
, considere como as
atualizações de conteúdo funcionam. Se os dados forem carregados diretamente de um banco de dados
da 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 a
DataSource
usada mais
recentemente. Você precisa solicitar uma nova instância dessa fonte de dados. O snippet de código a seguir
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)
- Amostra de paginação com rede (link em inglês)
Codelabs
Vídeos
- Android Jetpack: gerenciar listas infinitas com RecyclerView e Paging (Google I/O 2018)
- 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