>

Architecture Components: Paging Library (Parte I) #AndroidMeetsKotlin

Francisco Manuel López Jurado     Colaboraciones    04/04/2019

Siguiendo la línea de Architecture Components, ahora vas a implementar una librería poco conocida llamada Paging Library. La implementación que vas a realizar consistirá en añadir a la parte de tu DataSource / Repository la capacidad de paginación. Puesto que el contenido es muy amplio, en esta sección verás los cambios que debes aplicar en build.gradle para añadir la dependencia, DAO, GithubRepoLocalDataSource y creación del GithubRepoBoundaryCallback. En el siguiente post terminaremos su integración, no obstante, si estás ansioso por verla al completo, tienes todo el código disponible en el siguiente enlace bajo la rama “paging”. Sin más, ¡Manos a la obra!

Objetivo

El principal objetivo de esta librería es facilitar la carga paginada de datos en un listado. Anteriormente, siempre has tenido que usar una librería de terceros o incluso tu propia implementación, la cual podía contener fallos y no acoplarse perfectamente a la arquitectura planteada por Google. Gracias a Paging Library, el acople es perfecto y sigue todos los estándares marcados por la compañía.

Nuevos componentes

Los principales componentes que te vas a encontrar son:

  • PagidList. Simplificando, se trata de una colección que carga datos paginados asíncronamente. Puede usarse para cargar datos desde los DataSources que definas.
  • LivePagedListBuilder. Crea un LiveData basado en DataSource.Factory y una configuración mediante PagedList.Config.
  • BoundaryCallback. Marca cuando un PagedList ha llegado al final de sus datos disponibles.
  • PagidListAdapter. Basado en RecyclerView.Adapter, presenta los datos paginados en un RecyclerView. Además, tiene los distintos callbacks de PagedList para saber cuando los datos son cargados y utiliza DiffUtil para calcular los nuevos datos cargados.

Aplicando cambios

Continuarás el proyecto que has ido creando siguiendo los anteriores posts (Architecture Components: Repository y DataSources #AndroidMeetsKotlin de Fran López), pero en este caso debes utilizar la rama “paging” para separar las distintas versiones. Los cambios que debes hacer son:

1.- Añade la dependencia en build.gradle

Tienes que añadir la siguiente dependencia, en tu caso “pagingVersion” es igual a “1.0.1”.

2.- Cambios en el DAO

El método “getReposAsync” ya no retornará un LiveData<List> sino un DataSource.Factory<Int, GithubRepoDomain>. De esta forma, el Int será la clave y tendrá la información de la posición en el PagedList (PositionalDataSource).

También debes añadir un método que te retornará la lista de repositorios de forma directa, esto lo usarás para saber la página que has consultado en una consulta anterior en base a los datos ya guardados en base de datos. Este método quedará de la siguiente forma:

La implementación la puedes encontrar en el siguiente enlace.

3.- Cambios en el DataSource

El cambio anterior obliga a modificar la implementación de la clase GithubRepoLocalDataSource, concretamente el método “getGithubRepos”, éste debe retornar también DataSource.Factory<Int, GithubRepoDomain>. Además, tendrá un método adicional para obtener los datos directamente sin la necesidad de un LiveData que usarás más adelante.

La implementación la puedes encontrar en el siguiente enlace.

4.- Crear el GithubRepoBoundaryCallback

Su objetivo es el de calcular cuándo se ha llegado al final de los datos disponibles. En el momento que un DataSource se queda sin datos para cargar, se llamará al método “onItemAtEndLoaded(Object)”. En este caso, debes llamar a GithubRepoApiDataSource para que empiece la carga de datos desde los servicios Web. Una vez que los datos lleguen de forma satisfactoria, se guardarán en BD y, debido a que está observando la BD con el LiveData, provocará que la UI se actualice de inmediato con los datos nuevos.

Es importante marcar cuando una llamada asíncrona ya está en curso para que no hagas una nueva. En cuanto a sus métodos, sólo tiene tres métodos:

  • onItemAtEndLoaded(T itemAtEnd). Se llama cuando el elemento al final de PagedList se ha cargado y el acceso se ha producido dentro de la distancia de precarga (“prefetchDistance”) establecido en su creación.
  • onZeroItemsLoaded(). Se llama cuando se devuelven cero elementos desde una carga inicial de la fuente de datos de PagedList.
  • onItemAtFrontLoaded(T itemAtFront). Lo mismo que “onItemAtEndLoaded” pero para el caso del primer elemento.

Para este ejemplo, necesitas que tu BoundaryCallback, al que llamarás “GithubRepoBoundaryCallback”, tenga los siguientes parámetros:

  • query. Se trata de la consulta a realizar.
  • githubRepoApiDataSource. Lo utilizarás para cargar los siguientes datos.
  • githubRepoLocalDataSource. Lo usarás para almacenar los datos en BD cuando sean retornados del servicio.
  • isRequesting. Controla si hay una llamada en curso. Este flag anteriormente se encontraba en el Repository, por lo que debes borrarlo en él.
  • lastPageRequestedNumber. Tiene la información de la última página consultada al servicio.

La implementación que harás al sobreescribir los métodos “onZeroItemsLoaded” y “onItemAtEndLoaded” debe ser la de llamar al servicio para obtener la información necesaria. Éstos quedarían tal que así:

Para tu caso, no necesitarás sobreescribir onItemAtFrontLoaded.

En cuanto al método “requestNewDataAndSave(query: String)”, debe marcar el boolean a true al inicio del mismo y realizará la llamada pasándole la consulta y la página. Una vez que hemos obtenido una respuesta correcta, inserta los datos a la BD e incrementa el número de página. Tanto si la llamada ha ido bien como si ha ido mal, marcaremos el flag de consulta a false. El código quedaría tal que así:

La implementación la puedes encontrar en el siguiente enlace.

Hasta aquí por ahora

Resumiendo, después de todo lo anterior, tienes una base sólida para poder finalizar la implementación en el siguiente post. Como has podido ver, los cambios no son complejos y permiten no perderte por una documentación extensa.


Sobre el autor

Francisco Manuel López Jurado   

Apasionado de la tecnología y todo lo que la rodea. Desarrollador Senior Android e iOS.