Aspectos básicos de Kotlin para Android 09.2: WorkManager

Este codelab es parte del curso Conceptos básicos de Kotlin para Android. Aprovecharás al máximo este curso si trabajas con los codelabs de forma secuencial. Todos los codelabs del curso se enumeran en la página de destino de los codelabs de Android Kotlin Fundamentals.

Introducción

La mayoría de las apps del mundo real necesitan realizar tareas en segundo plano de larga duración. Por ejemplo, una app podría subir archivos a un servidor, sincronizar datos desde un servidor y guardarlos en una base de datos de Room, enviar registros a un servidor o ejecutar operaciones costosas en los datos. Estas operaciones deben realizarse en segundo plano, fuera del subproceso de IU (subproceso principal). Las tareas en segundo plano consumen los recursos limitados de un dispositivo, como la RAM y la batería. Esto puede causar una experiencia del usuario deficiente si no se maneja de forma adecuada.

En este codelab, aprenderás a usar WorkManager para programar una tarea en segundo plano de manera optimizada y eficiente. Si deseas obtener más información sobre otras soluciones disponibles para el procesamiento en segundo plano en Android, consulta la Guía para el procesamiento en segundo plano.

Conocimientos que ya deberías tener

  • Cómo usar los componentes de la arquitectura de Android ViewModel, LiveData y Room
  • Cómo realizar transformaciones en una clase LiveData
  • Cómo compilar y lanzar una corrutina
  • Cómo usar adaptadores de vinculación en la vinculación de datos
  • Cómo cargar datos almacenados en caché con un patrón de repositorio

Qué aprenderás

  • Cómo crear un Worker, que representa una unidad de trabajo
  • Cómo crear un WorkRequest para solicitar que se realice un trabajo
  • Cómo agregar restricciones al WorkRequest para definir cómo y cuándo se debe ejecutar un worker
  • Cómo usar WorkManager para programar tareas en segundo plano

Actividades

  • Crea un worker para ejecutar una tarea en segundo plano que recupere previamente la playlist de videos de DevBytes de la red.
  • Programa el trabajador para que se ejecute de forma periódica.
  • Agrega restricciones al WorkRequest.
  • Programa un WorkRequest periódico que se ejecute una vez al día.

En este codelab, trabajarás en la app de DevBytes que desarrollaste en un codelab anterior. (Si no tienes esta app, puedes descargar el código de partida para esta lección).

La app de DevBytes muestra una lista de videos de DevBytes, que son instructivos breves creados por el equipo de relaciones con desarrolladores de Android de Google. En los videos, se presentan las funciones para desarrolladores y las prácticas recomendadas para el desarrollo de Android.

Mejoraste la experiencia del usuario en la app realizando una recuperación previa de los videos una vez al día. Esto garantiza que el usuario obtenga contenido nuevo en cuanto abra la app.

En esta tarea, descargarás y revisarás el código de partida.

Paso 1: Descarga y ejecuta la app de inicio

Puedes seguir trabajando en la app de DevBytes que compilaste en el codelab anterior (si la tienes). También puedes descargar la app de inicio.

En esta tarea, descargarás y ejecutarás la app de inicio, y examinarás el código de inicio.

  1. Si aún no tienes la app de DevBytes, descarga el código de partida de DevBytes para este codelab desde el proyecto DevBytesRepository de GitHub.
  2. Descomprime el código y abre el proyecto en Android Studio.
  3. Conecta tu dispositivo de prueba o emulador a Internet si aún no lo está. Compila y ejecuta la app. La app recupera la lista de videos de DevByte de la red y los muestra.
  4. En la app, presiona cualquier video para abrirlo en la app de YouTube.

Paso 2: Explora el código

La app de inicio incluye mucho código que se presentó en el codelab anterior. El código de partida de este codelab tiene módulos de redes, interfaz de usuario, caché sin conexión y repositorio. Puedes enfocarte en programar la tarea en segundo plano con WorkManager.

  1. En Android Studio, expande todos los paquetes.
  2. Explora el paquete database. El paquete contiene las entidades de la base de datos y la base de datos local, que se implementa con Room.
  3. Explora el paquete repository. El paquete contiene la clase VideosRepository que abstrae la capa de datos del resto de la app.
  4. Explora el resto del código de partida por tu cuenta y con la ayuda del codelab anterior.

WorkManager es uno de los componentes de la arquitectura de Android y parte de Android Jetpack. WorkManager es para el trabajo en segundo plano que se puede diferir y requiere una ejecución garantizada:

  • Diferible significa que el trabajo no debe ejecutarse de inmediato. Por ejemplo, enviar datos analíticos al servidor o sincronizar la base de datos en segundo plano son tareas que se pueden aplazar.
  • La ejecución garantizada significa que la tarea se ejecutará incluso si se cierra la app o se reinicia el dispositivo.

Mientras WorkManager ejecuta el trabajo en segundo plano, se encarga de los problemas de compatibilidad y las prácticas recomendadas para la batería y el estado del sistema. WorkManager ofrece compatibilidad hasta el nivel de API 14. WorkManager elige una forma adecuada de programar una tarea en segundo plano, según el nivel de API del dispositivo. Puede usar JobScheduler (en el nivel de API 23 y versiones posteriores) o una combinación de AlarmManager y BroadcastReceiver.

WorkManager también te permite establecer criterios sobre cuándo se ejecuta la tarea en segundo plano. Por ejemplo, es posible que quieras que la tarea se ejecute solo cuando el estado de la batería, el estado de la red o el estado de carga cumplan con ciertos criterios. Aprenderás a establecer restricciones más adelante en este codelab.

En este codelab, programarás una tarea para recuperar previamente la lista de reproducción de videos de DevBytes de la red una vez al día. Para programar esta tarea, usa la biblioteca WorkManager.

  1. Abre el archivo build.gradle (Module:app) y agrega la dependencia WorkManager al proyecto.

    Si usas la versión más reciente de la biblioteca, la app de solución debería compilarse según lo esperado. Si no es así, intenta resolver el problema o vuelve a la versión de la biblioteca que se muestra a continuación.
// WorkManager dependency
def work_version = "1.0.1"
implementation "android.arch.work:work-runtime-ktx:$work_version"
  1. Sincroniza tu proyecto y asegúrate de que no haya errores de compilación.

Antes de agregar código al proyecto, familiarízate con las siguientes clases de la biblioteca WorkManager:

  • Worker
    En esta clase, defines el trabajo real (la tarea) que se ejecutará en segundo plano. Extiendes esta clase y anulas el método doWork(). El método doWork() es donde colocas el código que se ejecutará en segundo plano, como la sincronización de datos con el servidor o el procesamiento de imágenes. En esta tarea, implementarás Worker.
  • WorkRequest
    Esta clase representa una solicitud para ejecutar el trabajador en segundo plano. Usa WorkRequest para configurar cómo y cuándo ejecutar la tarea del trabajador, con la ayuda de Constraints, como el dispositivo enchufado o la conexión Wi-Fi. Implementarás WorkRequest en una tarea posterior.
  • WorkManager
    Esta clase programa y ejecuta tu WorkRequest. WorkManager programa solicitudes de trabajo de manera que se distribuya la carga sobre los recursos del sistema, respetando las restricciones que hayas especificado. Implementarás WorkManager en una tarea posterior.

Paso 1: Crea un trabajador

En esta tarea, agregarás un Worker para recuperar previamente la playlist de videos de DevBytes en segundo plano.

  1. Dentro del paquete devbyteviewer, crea un paquete nuevo llamado work.
  2. En el paquete work, crea una nueva clase de Kotlin llamada RefreshDataWorker.
  3. Extiende la clase RefreshDataWorker desde CoroutineWorker. Pasa context y WorkerParameters como parámetros del constructor.
class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
       CoroutineWorker(appContext, params) {
}
  1. Para resolver el error de clase abstracta, anula el método doWork() dentro de la clase RefreshDataWorker.
override suspend fun doWork(): Result {
  return Result.success()
}

Una función de suspensión es una función que se puede pausar y reanudar más tarde. Una función de suspensión puede ejecutar una operación de larga duración y esperar a que se complete sin bloquear el subproceso principal.

Paso 2: Implementa doWork()

Se llama al método doWork() dentro de la clase Worker en un subproceso en segundo plano. El método realiza el trabajo de forma síncrona y debe devolver un objeto ListenableWorker.Result. El sistema Android le otorga a un Worker un máximo de 10 minutos para finalizar su ejecución y devolver un objeto ListenableWorker.Result. Una vez que vence este tiempo, el sistema detiene de forma forzosa el Worker.

Para crear un objeto ListenableWorker.Result, llama a uno de los siguientes métodos estáticos para indicar el estado de finalización del trabajo en segundo plano:

En esta tarea, implementarás el método doWork() para recuperar la playlist de videos de DevBytes de la red. Puedes reutilizar los métodos existentes en la clase VideosRepository para recuperar los datos de la red.

  1. En la clase RefreshDataWorker, dentro de doWork(), crea una instancia de un objeto VideosDatabase y un objeto VideosRepository.
override suspend fun doWork(): Result {
   val database = getDatabase(applicationContext)
   val repository = VideosRepository(database)

   return Result.success()
}
  1. En la clase RefreshDataWorker, dentro de doWork(), sobre la sentencia return, llama al método refreshVideos() dentro de un bloque try. Agrega un registro para hacer un seguimiento de cuándo se ejecuta el trabajador.
try {
   repository.refreshVideos( )
   Timber.d("Work request for sync is run")
   } catch (e: HttpException) {
   return Result.retry()
}

Para resolver el error "Unresolved reference", importa retrofit2.HttpException.

  1. Aquí se encuentra la clase RefreshDataWorker completa para tu referencia:
class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
       CoroutineWorker(appContext, params) {

   override suspend fun doWork(): Result {
       val database = getDatabase(applicationContext)
       val repository = VideosRepository(database)
       try {
           repository.refreshVideos()
       } catch (e: HttpException) {
           return Result.retry()
       }
       return Result.success()
   }
}

Un Worker define una unidad de trabajo, y el WorkRequest define cómo y cuándo se debe ejecutar el trabajo. Existen dos implementaciones concretas de la clase WorkRequest:

  • La clase OneTimeWorkRequest sirve para tareas únicas. (Una tarea única ocurre solo una vez).
  • La clase PeriodicWorkRequest sirve para trabajo periódico y para tareas que se repiten por intervalos.

Las tareas pueden ser únicas o periódicas, así que elige la clase según corresponda. Para obtener más información sobre la programación de trabajos recurrentes, consulta la documentación relacionada.

En esta tarea, definirás y programarás un WorkRequest para ejecutar el worker que creaste en la tarea anterior.

Paso 1: Configura el trabajo recurrente

En una app para Android, la clase Application es la clase base que contiene todos los demás componentes, como actividades y servicios. Cuando se crea el proceso para tu aplicación o paquete, se crea una instancia de la clase Application (o cualquier subclase de Application) antes que cualquier otra clase.

En esta app de ejemplo, la clase DevByteApplication es una subclase de la clase Application. La clase DevByteApplication es un buen lugar para programar el WorkManager.

  1. En la clase DevByteApplication, crea un método llamado setupRecurringWork() para configurar el trabajo recurrente en segundo plano.
/**
* Setup WorkManager background job to 'fetch' new network data daily.
*/
private fun setupRecurringWork() {
}
  1. Dentro del método setupRecurringWork(), crea e inicializa una solicitud de trabajo periódica para que se ejecute una vez al día con el método PeriodicWorkRequestBuilder(). Pasa la clase RefreshDataWorker que creaste en la tarea anterior. Pasa un intervalo de repetición de 1 con una unidad de tiempo de TimeUnit.DAYS.
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
       .build()

Para resolver el error, importa java.util.concurrent.TimeUnit.

Paso 2: Programa una WorkRequest con WorkManager

Después de definir tu WorkRequest, puedes programarla con WorkManager usando el método enqueueUniquePeriodicWork(). Este método te permite agregar un PeriodicWorkRequest con un nombre único a la fila, en la que solo puede haber un PeriodicWorkRequest con un nombre determinado activo a la vez.

Por ejemplo, es posible que solo quieras que una operación de sincronización esté activa. Si hay una operación de sincronización pendiente, puedes dejar que se ejecute o reemplazarla por tu nuevo trabajo con un ExistingPeriodicWorkPolicy.

Para obtener más información sobre las formas de programar un WorkRequest, consulta la documentación de WorkManager.

  1. En la clase RefreshDataWorker, al comienzo de la clase, agrega un objeto complementario. Define un nombre de trabajador para identificarlo de forma única.
companion object {
   const val WORK_NAME = "com.example.android.devbyteviewer.work.RefreshDataWorker"
}
  1. En la clase DevByteApplication, al final del método setupRecurringWork(), programa el trabajo con el método enqueueUniquePeriodicWork(). Pasa el enum KEEP para ExistingPeriodicWorkPolicy. Pasa repeatingRequest como el parámetro PeriodicWorkRequest.
WorkManager.getInstance().enqueueUniquePeriodicWork(
       RefreshDataWorker.WORK_NAME,
       ExistingPeriodicWorkPolicy.KEEP,
       repeatingRequest)

Si existe trabajo pendiente (incompleto) con el mismo nombre, el parámetro ExistingPeriodicWorkPolicy.KEEP hace que WorkManager conserve el trabajo periódico anterior y descarte la nueva solicitud de trabajo.

  1. Al comienzo de la clase DevByteApplication, crea un objeto CoroutineScope. Pasa Dispatchers.Default como parámetro del constructor.
private val applicationScope = CoroutineScope(Dispatchers.Default)
  1. En la clase DevByteApplication, agrega un método nuevo llamado delayedInit() para iniciar una corrutina.
private fun delayedInit() {
   applicationScope.launch {
   }
}
  1. Dentro del método delayedInit(), llama a setupRecurringWork().
  2. Mueve la inicialización de Timber del método onCreate() al método delayedInit().
private fun delayedInit() {
   applicationScope.launch {
       Timber.plant(Timber.DebugTree())
       setupRecurringWork()
   }
}
  1. En la clase DevByteApplication, al final del método onCreate(), agrega una llamada al método delayedInit().
override fun onCreate() {
   super.onCreate()
   delayedInit()
}
  1. Abre el panel Logcat en la parte inferior de la ventana de Android Studio. Filtrar por RefreshDataWorker
  2. Ejecuta la app. WorkManager programa tu trabajo recurrente de inmediato.

    En el panel Logcat, observa las instrucciones de registro que muestran que la solicitud de trabajo se programó y, luego, se ejecutó correctamente.
D/RefreshDataWorker: Work request for sync is run
I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]

El registro WM-WorkerWrapper se muestra desde la biblioteca WorkManager, por lo que no puedes cambiar este mensaje de registro.

Paso 3: (Opcional) Programa el WorkRequest para un intervalo mínimo

En este paso, disminuyes el intervalo de tiempo de 1 día a 15 minutos. Esto te permitirá ver los registros de una solicitud de trabajo periódica en acción.

  1. En la clase DevByteApplication, dentro del método setupRecurringWork(), comenta la definición actual de repeatingRequest. Agrega una nueva solicitud de trabajo con un intervalo de repetición periódico de 15 minutos.
// val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
//        .build()
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(15, TimeUnit.MINUTES)
       .build()
  1. Abre el panel Logcat en Android Studio y filtra por RefreshDataWorker. Para borrar los registros anteriores, haz clic en el ícono Borrar logcat .
  2. Ejecuta la app y WorkManager programará tu trabajo recurrente de inmediato. En el panel Logcat, observa los registros: la solicitud de trabajo se ejecuta una vez cada 15 minutos. Espera 15 minutos para ver otro conjunto de registros de solicitudes de trabajo. Puedes dejar la app en ejecución o cerrarla. El administrador de trabajo debería seguir ejecutándose.

    Ten en cuenta que, a veces, el intervalo es inferior a 15 minutos y, otras veces, superior. (El momento exacto está sujeto a las optimizaciones de batería del SO).
12:44:40 D/RefreshDataWorker: Work request for sync is run
12:44:40 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
12:59:24 D/RefreshDataWorker: Work request for sync is run
12:59:24 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
13:15:03 D/RefreshDataWorker: Work request for sync is run
13:15:03 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
13:29:22 D/RefreshDataWorker: Work request for sync is run
13:29:22 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
13:44:26 D/RefreshDataWorker: Work request for sync is run
13:44:26 I/WM-WorkerWrapper: Worker result SUCCESS for Work
 

¡Felicitaciones! Creaste un trabajador y programaste la solicitud de trabajo con WorkManager. Pero hay un problema: no especificaste ninguna restricción. WorkManager programará el trabajo una vez al día, incluso si el dispositivo tiene poca batería, está en suspensión o no tiene conexión de red. Esto afectará la batería y el rendimiento del dispositivo, y podría generar una mala experiencia del usuario.

En la próxima tarea, agregarás restricciones para abordar este problema.

En la tarea anterior, usaste WorkManager para programar una solicitud de trabajo. En esta tarea, agregarás criterios para determinar cuándo ejecutar el trabajo.

Cuando defines el WorkRequest, puedes especificar restricciones para el momento en que se debe ejecutar el Worker. Por ejemplo, puedes especificar que el trabajo solo se ejecute cuando el dispositivo esté inactivo o solo cuando esté enchufado y conectado a Wi-Fi. También puedes especificar una política de reintentos para volver a intentar el trabajo. Las restricciones admitidas son los métodos de configuración en Constraints.Builder. Para obtener más información, consulta Cómo definir tus solicitudes de trabajo.

Paso 1: Agrega un objeto Constraints y establece una restricción

En este paso, crearás un objeto Constraints y establecerás una restricción en el objeto, una restricción de tipo de red. (Es más fácil notar los registros con una sola restricción. En un paso posterior, agregarás otras restricciones.

  1. En la clase DevByteApplication, al comienzo de setupRecurringWork(), define un val del tipo Constraints. Usa el método Constraints.Builder().
val constraints = Constraints.Builder()

Para resolver el error, importa androidx.work.Constraints.

  1. Usa el método setRequiredNetworkType() para agregar una restricción de tipo de red al objeto constraints. Usa la enumeración UNMETERED para que la solicitud de trabajo solo se ejecute cuando el dispositivo esté en una red no medida.
.setRequiredNetworkType(NetworkType.UNMETERED)
  1. Usa el método build() para generar las restricciones del compilador.
val constraints = Constraints.Builder()
       .setRequiredNetworkType(NetworkType.UNMETERED)
       .build()

Ahora debes establecer el objeto Constraints recién creado en la solicitud de trabajo.

  1. En la clase DevByteApplication, dentro del método setupRecurringWork(), establece el objeto Constraints en la solicitud de trabajo periódica, repeatingRequest. Para establecer las restricciones, agrega el método setConstraints() sobre la llamada al método build().
       val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(15, TimeUnit.MINUTES)
               .setConstraints(constraints)
               .build()

Paso 2: Ejecuta la app y observa los registros

En este paso, ejecutarás la app y observarás que la solicitud de trabajo restringida se ejecuta en segundo plano en intervalos.

  1. Desinstala la app del dispositivo o emulador para cancelar las tareas programadas con anterioridad.
  2. Abre el panel Logcat en Android Studio. En el panel Logcat, borra los registros anteriores haciendo clic en el ícono Clear logcat a la izquierda. Filtrar por work
  3. Desactiva la conexión Wi-Fi en el dispositivo o el emulador para que puedas ver cómo funcionan las restricciones. El código actual solo establece una restricción, lo que indica que la solicitud solo debe ejecutarse en una red sin medición. Como el Wi-Fi está desactivado, el dispositivo no está conectado a la red, ya sea de uso medido o no medido. Por lo tanto, no se cumplirá esta restricción.
  4. Ejecuta la app y observa el panel de Logcat. WorkManager programa la tarea en segundo plano de inmediato. Como no se cumple la restricción de red, no se ejecuta la tarea.
11:31:44 D/DevByteApplication: Periodic Work request for sync is scheduled
  1. Activa la conexión Wi-Fi en el dispositivo o el emulador y observa el panel Logcat. Ahora, la tarea en segundo plano programada se ejecuta aproximadamente cada 15 minutos, siempre que se cumpla la restricción de red.
11:31:44 D/DevByteApplication: Periodic Work request for sync is scheduled
11:31:47 D/RefreshDataWorker: Work request for sync is run
11:31:47 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]
11:46:45 D/RefreshDataWorker: Work request for sync is run
11:46:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:03:05 D/RefreshDataWorker: Work request for sync is run
12:03:05 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:16:45 D/RefreshDataWorker: Work request for sync is run
12:16:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:31:45 D/RefreshDataWorker: Work request for sync is run
12:31:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:47:05 D/RefreshDataWorker: Work request for sync is run
12:47:05 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
13:01:45 D/RefreshDataWorker: Work request for sync is run
13:01:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]

Paso 3: Agrega más restricciones

En este paso, agregarás las siguientes restricciones a PeriodicWorkRequest:

  • La batería no está baja.
  • Carga del dispositivo
  • Inactividad del dispositivo: Solo disponible en el nivel de API 23 (Android M) y versiones posteriores.

Implementa lo siguiente en la clase DevByteApplication.

  1. En la clase DevByteApplication, dentro del método setupRecurringWork(), indica que la solicitud de trabajo solo debe ejecutarse si la batería no está baja. Agrega la restricción antes de la llamada al método build() y usa el método setRequiresBatteryNotLow().
.setRequiresBatteryNotLow(true)
  1. Actualiza la solicitud de trabajo para que se ejecute solo cuando el dispositivo se esté cargando. Agrega la restricción antes de la llamada al método build() y usa el método setRequiresCharging().
.setRequiresCharging(true)
  1. Actualiza la solicitud de trabajo para que se ejecute solo cuando el dispositivo esté inactivo. Agrega la restricción antes de la llamada al método build() y usa el método setRequiresDeviceIdle(). Esta restricción ejecuta la solicitud de trabajo solo cuando el usuario no está usando el dispositivo de forma activa. Esta función solo está disponible en Android 6.0 (Marshmallow) y versiones posteriores, por lo que debes agregar una condición para la versión del SDK M y versiones posteriores.
.apply {
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
       setRequiresDeviceIdle(true)
   }
}

Esta es la definición completa del objeto constraints.

val constraints = Constraints.Builder()
       .setRequiredNetworkType(NetworkType.UNMETERED)
       .setRequiresBatteryNotLow(true)
       .setRequiresCharging(true)
       .apply {
           if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
               setRequiresDeviceIdle(true)
           }
       }
       .build()
  1. Dentro del método setupRecurringWork(), vuelve a cambiar el intervalo de solicitudes a una vez al día.
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
       .setConstraints(constraints)
       .build()

Aquí se muestra la implementación completa del método setupRecurringWork(), con un registro para que puedas hacer un seguimiento de cuándo se programa la solicitud de trabajo periódica.

private fun setupRecurringWork() {

       val constraints = Constraints.Builder()
               .setRequiredNetworkType(NetworkType.UNMETERED)
               .setRequiresBatteryNotLow(true)
               .setRequiresCharging(true)
               .apply {
                   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                       setRequiresDeviceIdle(true)
                   }
               }
               .build()
       val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
               .setConstraints(constraints)
               .build()
       
       Timber.d("Periodic Work request for sync is scheduled")
       WorkManager.getInstance().enqueueUniquePeriodicWork(
               RefreshDataWorker.WORK_NAME,
               ExistingPeriodicWorkPolicy.KEEP,
               repeatingRequest)
   }
  1. Para quitar la solicitud de trabajo programada anteriormente, desinstala la app de DevBytes de tu dispositivo o emulador.
  2. Ejecuta la app y WorkManager programará de inmediato la solicitud de trabajo. La solicitud de trabajo se ejecuta una vez al día, cuando se cumplen todas las restricciones.
  3. Esta solicitud de trabajo se ejecutará en segundo plano mientras la app esté instalada, incluso si no se está ejecutando. Por ese motivo, debes desinstalar la app del teléfono.

¡Bien hecho! Implementaste y programaste una solicitud de trabajo que no consume mucha batería para la recuperación previa diaria de videos en la app de DevBytes. WorkManager programará y ejecutará el trabajo, lo que optimizará los recursos del sistema. Tus usuarios y sus baterías estarán muy contentos.

Proyecto de Android Studio: DevBytesWorkManager

  • La API de WorkManager facilita la programación de tareas asíncronas diferibles que se deben ejecutar de manera confiable.
  • La mayoría de las apps del mundo real necesitan realizar tareas en segundo plano de larga duración. Para programar una tarea en segundo plano de manera optimizada y eficiente, usa WorkManager.
  • Las clases principales de la biblioteca WorkManager son Worker, WorkRequest y WorkManager.
  • La clase Worker representa una unidad de trabajo. Para implementar la tarea en segundo plano, extiende la clase Worker y anula el método doWork().
  • La clase WorkRequest representa una solicitud para realizar una unidad de trabajo. WorkRequest es la clase base para especificar parámetros para el trabajo que programas en WorkManager.
  • Hay dos implementaciones concretas de la clase WorkRequest: OneTimeWorkRequest para tareas únicas y PeriodicWorkRequest para solicitudes de trabajo periódicas.
  • Cuando definas el WorkRequest, puedes especificar Constraints para indicar cuándo se debe ejecutar el Worker. Las restricciones incluyen aspectos como si el dispositivo está enchufado, si está inactivo o si está conectado a Wi-Fi.
  • Para agregar restricciones al WorkRequest, usa los métodos de configuración que se indican en la documentación de Constraints.Builder. Por ejemplo, para indicar que WorkRequest no debe ejecutarse si la batería del dispositivo está baja, usa el método set setRequiresBatteryNotLow().
  • Después de definir el WorkRequest, transfiere la tarea al sistema Android. Para ello, programa la tarea con uno de los métodos enqueue de WorkManager.
  • La hora exacta en la que se ejecuta el Worker depende de las limitaciones que se usen en el WorkRequest y de las optimizaciones del sistema. WorkManager está diseñado para tener el mejor comportamiento posible, dadas estas restricciones.

Curso de Udacity:

Documentación para desarrolladores de Android:

Otro:

En esta sección, se enumeran las posibles actividades para el hogar para los alumnos que trabajan en este codelab como parte de un curso dirigido por un instructor. Depende del instructor hacer lo siguiente:

  • Si es necesario, asigna una tarea.
  • Comunicarles a los alumnos cómo enviar las actividades para el hogar.
  • Califica las actividades para el hogar.

Los instructores pueden usar estas sugerencias en la medida que quieran y deben asignar cualquier otra actividad para el hogar que consideren apropiada.

Si estás trabajando en este codelab por tu cuenta, usa estas actividades para el hogar para probar tus conocimientos.

Pregunta 1

¿Cuáles son las implicaciones concretas de la clase WorkRequest?

OneTimeWorkPeriodicRequest

OneTimeWorkRequest y PeriodicWorkRequest

OneTimeWorkRequest y RecurringWorkRequest

OneTimeOffWorkRequest y RecurringWorkRequest

Pregunta 2

¿Cuál de las siguientes clases usa WorkManager para programar la tarea en segundo plano en el nivel de API 23 y versiones posteriores?

▢ Solo JobScheduler

BroadcastReceiver y AlarmManager

AlarmManager y JobScheduler

Scheduler y BroadcastReceiver

Pregunta 3

¿Qué API se usa para agregar restricciones a un objeto WorkRequest?

setConstraints()

addConstraints()

setConstraint()

addConstraintsToWorkRequest()

Comienza la siguiente lección: 10.1 Estilos y temas

Para obtener vínculos a otros codelabs de este curso, consulta la página de destino de los codelabs de Conceptos básicos de Kotlin para Android.