Google se compromete a impulsar la igualdad racial para las comunidades afrodescendientes. Obtén información al respecto.

Cómo definir solicitudes de trabajo

En la guía de introducción, se describió cómo crear un elemento WorkRequest sencillo y ponerlo en cola.

En esta guía, aprenderás a definir y personalizar objetos WorkRequest para controlar casos de uso comunes, como los siguientes:

  • Cómo programar trabajos periódicos y de una sola ejecución
  • Cómo establecer restricciones de trabajos, como el uso de Wi-Fi o la carga del dispositivo
  • Cómo garantizar un retraso mínimo en la ejecución de trabajos
  • Cómo establecer estrategias de retirada y reintento
  • Cómo pasar datos de entrada a trabajos
  • Cómo agrupar trabajos relacionados mediante etiquetas

Descripción general

El trabajo se define en WorkManager a través de un objeto WorkRequest. Para programar trabajos con WorkManager, primero debes crear un objeto WorkRequest y, luego, ponerlo en cola.

Kotlin


val myWorkRequest = ...
WorkManager.getInstance(myContext).enqueue(myWorkRequest)

Java


WorkRequest myWorkRequest = ...
WorkManager.getInstance(myContext).enqueue(myWorkRequest);

El objeto WorkRequest contiene toda la información que necesita WorkManager para programar y ejecutar tu trabajo. Esto incluye las restricciones que deben cumplirse para su ejecución, los datos de programación, como retrasos o intervalos repetidos, la configuración de reintentos y, además, podría incluir datos de entrada si el trabajo se basa en ellos.

WorkRequest es una clase básica abstracta. Hay dos implementaciones derivadas de esta clase que puedes usar para crear la solicitud: OneTimeWorkRequest y PeriodicWorkRequest. Como lo indican sus nombres, OneTimeWorkRequest es útil para programar trabajos no repetitivos, mientras que PeriodicWorkRequest es más adecuado para trabajos que se repiten en algún intervalo.

Cómo programar trabajos de una sola ejecución

Para programar un trabajo simple, que no necesite configuración adicional, usa el método estático from:

Kotlin


val myWorkRequest = OneTimeWorkRequest.from(MyWork::class.java)

Java


WorkRequest myWorkRequest = OneTimeWorkRequest.from(MyWork.class);

Para trabajos más complejos, puedes usar un compilador.

Kotlin


val uploadWorkRequest: WorkRequest =
   OneTimeWorkRequestBuilder<MyWork>()
       // Additional configuration
       .build()

Java


WorkRequest uploadWorkRequest =
   new OneTimeWorkRequest.Builder(MyWork.class)
       // Additional configuration
       .build();

Cómo programar trabajos periódicos

En algunas ocasiones, tu app podría requerir que determinadas tareas se ejecuten de forma periódica. Por ejemplo, es posible que quieras crear copias de seguridad de tus datos, descargar contenido actualizado en tu app o subir registros a un servidor periódicamente.

A continuación, se muestra cómo puedes usar la implementación PeriodicWorkRequest para crear un objeto WorkRequest que se ejecute de manera periódica:

Kotlin


val saveRequest =
       PeriodicWorkRequestBuilder<SaveImageToFileWorker>(1, TimeUnit.HOURS)
    // Additional configuration
           .build()

Java


PeriodicWorkRequest saveRequest =
       new PeriodicWorkRequest.Builder(SaveImageToFileWorker.class, 1, TimeUnit.HOURS)
           // Constraints
           .build();

En este ejemplo, el trabajo está programado con un intervalo de una hora.

El período de intervalo corresponde al tiempo mínimo entre las repeticiones. La hora exacta a la que se ejecutará el trabajador dependerá de las restricciones que uses en tu objeto WorkRequest y de las optimizaciones que lleve a cabo el sistema.

Intervalos de ejecución flexibles

Si las características de tu trabajo lo hacen depender del tiempo de ejecución, puedes configurar PeriodicWorkRequest para que se ejecute en un período flexible dentro de cada período de intervalo, como se muestra en la figura 1.

Puedes establecer un intervalo flexible para un trabajo periódico. Debes definir un intervalo de repetición y un intervalo flexible que especifica una cantidad de tiempo determinada al final del intervalo que se repite. WorkManager intentará ejecutar tu trabajo en el transcurso del intervalo flexible de cada ciclo.

Figura 1: En el diagrama se muestran intervalos de repetición con períodos flexibles en los que se puede ejecutar el trabajo.

Para definir un trabajo periódico con un período flexible, debes crear un elemento PeriodicWorkRequest y pasar el objeto flexInterval junto con repeatInterval. El período flexible transcurre desde repeatInterval - flexInterval hasta el final del intervalo.

El siguiente ejemplo es un trabajo periódico programado para ejecutarse en los últimos 15 minutos de cada período de una hora.

Kotlin


val myUploadWork = PeriodicWorkRequestBuilder<SaveImageToFileWorker>(
       1, TimeUnit.HOURS, // repeatInterval (the period cycle)
       15, TimeUnit.MINUTES) // flexInterval
    .build()

Java


WorkRequest saveRequest =
       new PeriodicWorkRequest.Builder(SaveImageToFileWorker.class,
               1, TimeUnit.HOURS,
               15, TimeUnit.MINUTES)
           .build();

El intervalo de repetición debe ser mayor o igual que PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS, y el intervalo flexible debe ser mayor o igual que PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS.

Efectos de las restricciones en trabajos periódicos

Puedes aplicar restricciones a trabajos periódicos. Por ejemplo, puedes agregar una restricción para que tu solicitud de trabajo se ejecute solo cuando se esté cargando el dispositivo del usuario. En ese caso, no se ejecutará PeriodicWorkRequest a menos que se cumpla esta condición, incluso si ya pasó el tiempo del intervalo de repetición definido. Esto podría ocasionar que se omita o retrase la ejecución de determinada parte del trabajo si las condiciones no se cumplen durante el intervalo de ejecución.

Restricciones de trabajos

Las restricciones garantizan que el trabajo se aplace hasta que se cumplan las condiciones óptimas. Las siguientes restricciones están disponibles para WorkManager.

NetworkType Restringe el tipo de red que se necesita para ejecutar tu trabajo, por ejemplo, una red Wi-Fi (UNMETERED).
BatteryNotLow Si se establece como verdadero, no se ejecutará el trabajo cuando el dispositivo esté en modo de batería baja.
RequiresCharging Si se establece como verdadero, solo se ejecutará el trabajo cuando el dispositivo se esté cargando.
DeviceIdle Si se establece como verdadero, solo se ejecutará el trabajo cuando el dispositivo del usuario esté inactivo. Esto puede resultar útil para operaciones en lotes que, de otro modo, podrían afectar negativamente el rendimiento de otras apps que se estén ejecutando de manera activa en el dispositivo del usuario.
StorageNotLow Si se establece como verdadero, no se ejecutará el trabajo si el espacio de almacenamiento del dispositivo del usuario es demasiado bajo.

Para crear un conjunto de restricciones y asociarlo con algún trabajo, crea una instancia de Constraints a través de Contraints.Builder() y asígnala a tu WorkRequest.Builder().

Por ejemplo, en el siguiente código, se crea una solicitud de trabajo que solo se ejecutará cuando el dispositivo del usuario esté conectado a una red Wi-Fi y cargándose.

Kotlin


val constraints = Constraints.Builder()
   .setRequiredNetworkType(NetworkType.UNMETERED)
   .setRequiresCharging(true)
   .build()

val myWorkRequest: WorkRequest =
   OneTimeWorkRequestBuilder<MyWork>()
       .setConstraints(constraints)
       .build()

Java


Constraints constraints = new Constraints.Builder()
       .setRequiredNetworkType(NetworkType.UNMETERED)
       .setRequiresCharging(true)
       .build();

WorkRequest myWorkRequest =
       new OneTimeWorkRequest.Builder(MyWork.class)
               .setConstraints(constraints)
               .build();

Cuando se especifican varias restricciones, tu trabajo solo se ejecuta si se cumplen todas.

En caso de que una restricción deje de cumplirse mientras se está ejecutando tu trabajo, WorkManager detendrá a su trabajador. Se intentará ejecutar de nuevo cuando se cumplan todas las restricciones.

Trabajos retrasados

En el caso de que tu trabajo no tenga restricciones o que todas se cumplan cuando el trabajo esté en cola, el sistema podrá optar por ejecutarlo de inmediato. Si no quieres que el trabajo se ejecute de inmediato, puedes especificar que comience después de un retraso inicial mínimo.

A continuación, te mostramos un ejemplo de cómo configurar tu trabajo para que se ejecute al menos 10 minutos después de que se haya puesto en cola.

Kotlin


val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
   .setInitialDelay(10, TimeUnit.MINUTES)
   .build()

Java


WorkRequest myWorkRequest =
      new OneTimeWorkRequest.Builder(MyWork.class)
               .setInitialDelay(10, TimeUnit.MINUTES)
               .build();

Si bien el ejemplo muestra cómo establecer un retraso inicial para un elemento OneTimeWorkRequest, también puedes hacerlo para PeriodicWorkRequest. En ese caso, solo se retrasaría la primera ejecución de tu trabajo periódico.

Política de retirada y reintento

Si necesitas que WorkManager vuelva a realizar tu trabajo, puedes mostrar Result.retry() desde tu trabajador. Entonces, se reprograma tu trabajo según el retraso de retirada y la política de retirada.

  • El retraso de retirada especifica la cantidad mínima de tiempo que se debe esperar para volver a ejecutar el trabajo después del primer intento. Este valor no puede ser inferior a 10 segundos (o MIN_BACKOFF_MILLIS).

  • La política de retirada define cómo debería aumentar el retraso de retirada con el paso del tiempo para los siguientes reintentos. WorkManager admite 2 políticas de retirada: LINEAR y EXPONENTIAL.

Cada solicitud de trabajo tiene una política y un retraso de retirada. La política predeterminada es EXPONENTIAL con un retraso de 10 segundos, pero se puede anular en la configuración de tu solicitud de trabajo.

A continuación, puedes ver un ejemplo de cómo personalizar el retraso y la política de retirada.

Kotlin


val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
   .setBackoffCriteria(
       BackoffPolicy.LINEAR,
       OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
       TimeUnit.MILLISECONDS)
   .build()

Java


WorkRequest myWorkRequest =
       new OneTimeWorkRequest.Builder(MyWork.class)
               .setBackoffCriteria(
                       BackoffPolicy.LINEAR,
                       OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
                       TimeUnit.MILLISECONDS)
               .build();

En este ejemplo, el retraso inicial de retirada se establece en el valor mínimo permitido: 10 segundos. Dado que la política es LINEAR, el intervalo de reintento aumentará unos 10 segundos luego de cada intento. Por ejemplo, la primera ejecución que finaliza mostrando Result.retry(), se reintentará después de 10 segundos, luego, en 20, 30, 40, y así sucesivamente, siempre que el trabajo siga mostrando Result.retry() en cada intento. Si la política de retirada fuera EXPONENTIAL, la secuencia de duración de reintentos sería aproximadamente 20, 40, 80, etcétera.

Cómo etiquetar tu trabajo

Cada solicitud de trabajo tiene un identificador único, que más tarde servirá para poder identificar el trabajo a fin de cancelarlo u observar su progreso.

Si tienes un grupo de trabajos relacionados lógicamente, también puede resultarte útil etiquetar esos elementos. Esto te permitirá operar con un grupo de solicitudes de trabajo en conjunto.

Por ejemplo, WorkManager.cancelAllWorkByTag(String) cancela todas las solicitudes de trabajo que tienen una etiqueta en particular, y WorkManager.getWorkInfosByTag(String) muestra una lista de objetos WorkInfo que sirve para determinar el estado actual del trabajo.

En el siguiente código, se muestra cómo puedes agregar una etiqueta de "limpieza" a tu trabajo:

Kotlin


val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
   .addTag("cleanup")
   .build()

Java


WorkRequest myWorkRequest =
       new OneTimeWorkRequest.Builder(MyWork.class)
       .addTag("cleanup")
       .build();

Por último, se pueden agregar varias etiquetas a una misma solicitud de trabajo. Estas etiquetas se almacenan internamente como un conjunto de strings, que puedes obtener mediante WorkRequest.getTags().

Cómo asignar datos de entrada

Es posible que tu trabajo requiera datos de entrada para funcionar. Por ejemplo, un trabajo que controla la subida de una imagen puede requerir que se suba el URI de la imagen como entrada.

Los valores de entrada se almacenan como pares clave-valor en un objeto Data y se pueden configurar en la solicitud de trabajo. WorkManager enviará los Data de entrada a tu trabajo cuando lo ejecute. La clase Worker puede acceder a los argumentos de entrada llamando a Worker.getInputData(). En el siguiente código, se muestra cómo crear una instancia de Worker que requiere datos de entrada y cómo enviarla a la solicitud de trabajo.

Kotlin


// Define the Worker requiring input
class UploadWork(appContext: Context, workerParams: WorkerParameters)
   : Worker(appContext, workerParams) {

   override fun doWork(): Result {
       val imageUriInput =
           inputData.getString("IMAGE_URI") ?: return Result.failure()

       uploadFile(imageUriInput)
       return Result.success()
   }
   ...
}

// Create a WorkRequest for your Worker and sending it input
val myUploadWork = OneTimeWorkRequestBuilder<UploadWork>()
   .setInputData(workDataOf(
       "IMAGE_URI" to "http://..."
   ))
   .build()

Java


// Define the Worker requiring input
public class UploadWork extends Worker {

   public UploadWork(Context appContext, WorkerParameters workerParams) {
       super(appContext, workerParams);
   }

   @NonNull
   @Override
   public Result doWork() {
       String imageUriInput = getInputData().getString("IMAGE_URI");
       if(imageUriInput == null) {
           return Result.failure();
       }

       uploadFile(imageUriInput);
       return Result.success();
   }
   ...
}

// Create a WorkRequest for your Worker and sending it input
WorkRequest myUploadWork =
      new OneTimeWorkRequest.Builder(UploadWork.class)
           .setInputData(
               new Data.Builder()
                   .putString("IMAGE_URI", "http://...")
                   .build()
           )
           .build();

Del mismo modo, se puede utilizar la clase Data para generar un valor de retorno. Los datos de entrada y salida se explican de forma más detallada en la sección Parámetros de entrada y valores mostrados.

Próximos pasos

En la página Estados y observación, encontrarás más información sobre los estados de trabajo y cómo supervisar su progreso.