Esta guía se basa en la descripción general de la biblioteca de Paging y explica cómo personalizar la solución de carga de datos de tu app para satisfacer sus necesidades de arquitectura.
Crea una lista observable
Por lo general, el código de IU observa un objeto LiveData<PagedList>
(o, si usas RxJava2, un Flowable<PagedList>
o Observable<PagedList>
), que reside en el ViewModel
de tu app. Este objeto observable forma una conexión entre la presentación y el contenido de los datos de la lista de tu app.
Para crear uno de esos objetos observables de PagedList
, pasa una instancia de DataSource.Factory
a un objeto LivePagedListBuilder
o RxPagedListBuilder
. Un objeto DataSource
carga páginas para una sola PagedList
. La clase de fábrica crea instancias nuevas de PagedList
en respuesta a actualizaciones de contenido, como invalidaciones de tablas de bases de datos y actualizaciones de red. La biblioteca de persistencias de Room puede proporcionarte objetos DataSource.Factory
, aunque también puedes crear los tuyos.
En el siguiente fragmento de código, se muestra cómo crear una instancia de LiveData<PagedList>
en la clase ViewModel
usando las capacidades de compilación de DataSource.Factory
de 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();
Define tu propia configuración de paginación
Si, además, quieres configurar una LiveData<PagedList>
para casos avanzados, también puedes definir tu propia configuración de paginación. En especial, puedes definir los siguientes atributos:
- Tamaño de la página: cantidad de elementos en cada página
- Distancia de carga previa: dado el último elemento visible en la IU de una app, la cantidad de elementos más allá de ese último que la biblioteca de Paging debería intentar recuperar de antemano. Este valor debe ser varias veces mayor que el tamaño de la página.
- Presencia del marcador de posición: determina si la IU muestra marcadores de posición para los elementos de la lista que aún no terminaron de cargarse. Para obtener más información sobre los beneficios y las desventajas de usar marcadores de posición, consulta cómo proporcionar marcadores de posición en tu IU.
Si deseas tener más control sobre cuándo la biblioteca de Paging carga una lista desde la base de datos de la app, pasa un objeto Executor
personalizado a LivePagedListBuilder
, como se muestra en el siguiente fragmento de código:
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();
Elige el tipo de fuente de datos correcto
Es importante conectarse a la fuente de datos que mejor maneja la estructura de los datos de origen:
- Usa
PageKeyedDataSource
si las páginas que cargas tienen incorporadas las teclas siguiente/anterior. Por ejemplo, si cargas publicaciones de redes sociales desde la red, es posible que debas pasar un token denextPage
de una carga a otra posterior. - Usa
ItemKeyedDataSource
si necesitas usar datos del elemento N para recuperar el elemento N + 1. Por ejemplo, si quieres recuperar comentarios en conversaciones de una app de debate, es posible que debas pasar el ID del último comentario para obtener el contenido del siguiente. - Usa
PositionalDataSource
si necesitas recuperar páginas de datos desde cualquier ubicación que elijas en tu almacén de datos. Esta clase admite la solicitud de un conjunto de elementos de datos que comiencen desde la ubicación que selecciones. Por ejemplo, la solicitud podría mostrar los 50 elementos de datos y comenzar en la ubicación 1500.
Notifica cuando los datos no sean válidos
Cuando se usa la biblioteca de Paging, corresponde a la capa de datos notificar a las otras capas de tu app cuando una tabla o fila está inactiva. Para hacerlo, llama a invalidate()
desde la clase DataSource
que elegiste para tu app.
Crea tus propias fuentes de datos
Si usas una solución de datos local personalizada o si cargas datos directamente desde una red, puedes implementar una de las subclases de DataSource
. En el siguiente fragmento de código, se muestra una fuente de datos que está desconectada de la hora de inicio de un concierto determinado:
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); }
Luego, puedes cargar estos datos personalizados en objetos PagedList
creando una subclase concreta de DataSource.Factory
. En el siguiente fragmento de código, se muestra cómo generar instancias nuevas de la fuente de datos personalizada definida en el fragmento 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; } }
Considera cómo funcionan las actualizaciones de contenido
A medida que construyes objetos PagedList
observables, considera cómo funcionan las actualizaciones de contenido. Si cargas datos directamente desde una base de datos de Room, las actualizaciones se envían automáticamente a la IU de tu app.
Cuando se utiliza una API de red paginada, en general, tienes una interacción de usuario, como "deslizar para actualizar", que sirve como señal para invalidar la DataSource
que usaste recientemente. Luego, solicitas una instancia nueva de esa fuente de datos. En el siguiente fragmento de código, se demuestra este comportamiento:
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(); } }
Proporciona una asignación de datos
La biblioteca de Paging admite transformaciones basadas en elementos y en páginas de elementos cargados por una DataSource
.
En el siguiente fragmento de código, se asigna una combinación de nombre y fecha de concierto a una sola cadena que contiene tanto el nombre como la fecha:
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(); } }
Esto puede ser útil si deseas unir, convertir o preparar elementos después de cargarlos. Debido a que esos procesos se realizan en el ejecutor de la recuperación, puedes hacer un trabajo potencialmente costoso, como leer desde el disco o consultar una base de datos separada.
Envía comentarios
Usa estos recursos para compartir tus comentarios y tus ideas con nosotros:
- Herramienta de seguimiento de errores
- Informa los problemas para que podamos corregir los errores.
Recursos adicionales
Para obtener más información sobre la biblioteca de Paging, consulta los siguientes recursos.
Ejemplos
Codelabs
Videos
- Android Jetpack: Cómo administrar listas infinitas con RecyclerView y Paging (Google I/O 2018)
- Android Jetpack: Paging
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Cómo migrar a Paging 3
- Descripción general de la biblioteca de Paging 2
- Cómo mostrar listas paginadas