Cómo migrar a Paging 3

Paging 3 es bastante diferente de las versiones anteriores de la biblioteca de paginación. Esta versión proporciona funciones mejoradas, compatibilidad de primer nivel con las corrutinas de Kotlin y Flow, y una integración perfecta con Jetpack Compose.

Beneficios de migrar a Paging 3

Paging 3 incluye las siguientes características que no estaban presentes en versiones anteriores de la biblioteca:

  • Compatibilidad de primer nivel con las corrutinas de Kotlin y Flow.
  • Estado de carga integrado y señales de error para un diseño de IU adaptable, incluida la función de reintento y actualización
  • Mejoras en la capa del repositorio, como la compatibilidad para la cancelación y una interfaz de fuente de datos simplificada
  • Mejoras en la capa de presentación, los separadores de lista, las transformaciones de páginas personalizadas, los encabezados y pies de página, y los elementos de estado de carga para las listas diferidas

Migra tu app a Paging 3

Para realizar la migración completa a Paging 3, debes migrar estos componentes principales de Paging 2:

  • Clases DataSource
  • PagedList
  • Capa de presentación (para LazyPagingItems)

Sin embargo, algunos componentes de Paging 3 son retrocompatibles con versiones anteriores de Paging. En particular, la API de Pager puede usar objetos DataSource más antiguos con el método asPagingSourceFactory. Esto significa que tienes las siguientes opciones de migración:

  • Puedes migrar tu DataSource a PagingSource, pero no modificar el resto de tu implementación de Paging.
  • Puedes migrar toda la implementación de Paging para migrar completamente tu app a Paging 3.

En las secciones de esta página, se explica cómo migrar los componentes de Paging de cada capa de tu app.

Clases DataSource

En esta sección, se describen todos los cambios necesarios para migrar una implementación de Paging más antigua a fin de usar PagingSource.

Los elementos PageKeyedDataSource, PositionalDataSource y ItemKeyedDataSource de Paging 2 se combinan en la API de PagingSource de Paging 3. Los métodos de carga de todas las clases de API anteriores se combinan en un único método load en PagingSource. Esto reduce la duplicación de código, ya que gran parte de la lógica de los métodos de carga en las implementaciones de las clases de API anteriores suele ser idéntica.

Todos los parámetros de métodos de carga se reemplazan en Paging 3 con una clase sellada LoadParams, que incluye subclases para cada tipo de carga. Si necesitas diferenciar los tipos de carga de tu método load, verifica qué subclase de LoadParams se pasó en LoadParams.Refresh, LoadParams.Prepend o LoadParams.Append.

Para obtener más información sobre la implementación de PagingSource, consulta Cómo definir una fuente de datos.

Actualiza las claves

Las implementaciones de PagingSource deben definir cómo se reanudan las actualizaciones desde el medio de los datos paginados cargados. Para ello, implementa getRefreshKey a fin de mapear la clave inicial correcta con state.anchorPosition como el índice al que se accedió más recientemente.

// Replaces ItemKeyedDataSource.
override fun getRefreshKey(state: PagingState<String, User>): String? {
  return state.anchorPosition?.let { anchorPosition ->
    state.getClosestItemToPosition(anchorPosition)?.id
  }
}

// Replacing PositionalDataSource.
override fun getRefreshKey(state: PagingState<Int, User>): Int? {
  return state.anchorPosition
}

Transformaciones de lista

En las versiones anteriores de la biblioteca de Paging, la transformación de los datos paginados se basa en los siguientes métodos:

  • DataSource.map
  • DataSource.mapByPage
  • DataSource.Factory.map
  • DataSource.Factory.mapByPage

En Paging 3, todas las transformaciones se aplican como operadores en PagingData. Si usas alguno de los métodos de la lista anterior para transformar tu lista paginada, debes mover la lógica de transformación de la DataSource a los PagingData cuando construyas la Pager con tu PagingSource nuevo.

Si quieres obtener más información para aplicar transformaciones a datos paginados con Paging 3, consulta Cómo transformar flujos de datos.

PagedList

En esta sección, se describen todos los cambios necesarios para migrar una implementación de Paging más antigua y usar Pager y PagingData en Paging 3.

Clases PagedListBuilder

PagingData reemplaza a la PagedList existente de Paging 2. Para migrar a PagingData, debes actualizar lo siguiente:

  • La configuración de la paginación se movió de PagedList.Config a PagingConfig.
  • Las clases de compilador anteriores se combinaron en una sola clase Pager.
  • Pager expone un Flow<PagingData> observable con su propiedad .flow.
val flow = Pager(
  // Configure how data is loaded by passing additional properties to
  // PagingConfig, such as prefetchDistance.
  PagingConfig(pageSize = 20)
) {
  ExamplePagingSource(backend, query)
}.flow
  .cachedIn(viewModelScope)

Si quieres obtener más información para configurar un flujo reactivo de objetos PagingData con Paging 3, consulta Cómo configurar un flujo de PagingData.

BoundaryCallback para fuentes en capas

En Paging 3, RemoteMediator reemplaza PagedList.BoundaryCallback como un controlador de la paginación desde la red y la base de datos.

Si quieres obtener más información para usar RemoteMediator para paginar desde la red y la base de datos en Paging 3, consulta el codelab de paginación de Android.

LazyPagingItems

En esta sección, se describen todos los cambios necesarios para migrar una implementación de Paging más antigua y usar LazyPagingItems en Paging 3.

Paging 3 proporciona collectAsLazyPagingItems para controlar el nuevo flujo de PagingData. Para migrar tu capa de presentación, usa el artefacto paging-compose y collectAsLazyPagingItems para recopilar elementos PagingData y mostrarlos en funciones @Composable.

Para obtener más información sobre LazyPagingItems, consulta Cómo cargar y mostrar datos paginados.

Actualizaciones y comparación de listas

Si actualmente usas lógica personalizada de comparación de listas, migra tu implementación para usar el LazyPagingItems proporcionado en Paging 3 en su lugar. Para asegurarte de que la diferenciación se realice correctamente, especifica una clave de elemento en tu lista diferida:

@Composable
fun UserScreen(viewModel: UserViewModel) {
    // Collects the Flow into a LazyPagingItems object
    val lazyPagingItems = viewModel.pager.flow.collectAsLazyPagingItems()

    UserList(lazyPagingItems)
}

@Composable
fun UserScreen(viewModel: UserViewModel) {
    val lazyPagingItems = viewModel.pager.flow.collectAsLazyPagingItems()

    UserList(lazyPagingItems)
}

@Composable
fun UserList(lazyPagingItems: LazyPagingItems<User>) {
    LazyColumn {
        items(
            count = lazyPagingItems.itemCount,
            // Provide a stable key for each item, similar to DiffUtil in Views
            key = lazyPagingItems.itemKey { user -> user.id } 
        ) { index ->
            val user = lazyPagingItems[index]
            if (user != null) {
                UserRow(user = user)
            }
        }
    }
}

Para obtener más información sobre las claves de elementos, consulta Claves de elementos.

Estados de carga

En Paging 3, no necesitas un adaptador independiente para mostrar encabezados o pies de página para los estados de carga. El objeto LazyPagingItems expone una propiedad loadState que puedes verificar directamente dentro de tu LazyColumn.

LazyColumn {
    // ... items(lazyPagingItems) go here ...

    // Show loading spinner at bottom of list when appending data
    if (lazyPagingItems.loadState.append is LoadState.Loading) {
        item {
            CircularProgressIndicator(modifier = Modifier.fillMaxWidth())
        }
    }
}

Recursos adicionales

Para obtener más información sobre la biblioteca de Paging, consulta los siguientes recursos adicionales:

Documentación

Mira contenido