Trabajos en cadena

WorkManager te permite crear y poner en cola una cadena de trabajo que especifica varias tareas dependientes. Además, define el orden en el que deberían ejecutarse. Esta funcionalidad resulta particularmente útil cuando necesitas ejecutar varias tareas en un orden específico.

A fin de crear una cadena de trabajo, puedes usar WorkManager.beginWith(OneTimeWorkRequest) o WorkManager.beginWith(List<OneTimeWorkRequest>), que mostrarán una instancia de WorkContinuation cada uno.

Luego, puedes usar un objeto WorkContinuation para agregar instancias dependientes de OneTimeWorkRequest mediante then(OneTimeWorkRequest) o then(List<OneTimeWorkRequest>).

Cada invocación de WorkContinuation.then(...) muestra una nueva instancia de WorkContinuation. Si agregas un elemento List de las instancias de OneTimeWorkRequest, estas solicitudes pueden ejecutarse en paralelo.

Por último, puedes usar el método WorkContinuation.enqueue() para poner en cola (enqueue()) tu cadena de WorkContinuation.

Veamos un ejemplo. En él se configuran 3 trabajos con distintos trabajadores para ejecutarse (posiblemente en paralelo). Luego, se unen los resultados de estos trabajadores y se pasan a un trabajo que se almacena en caché. Por último, el resultado de ese trabajo se pasa a un trabajador de carga, que sube los resultados a un servidor remoto.

Kotlin

WorkManager.getInstance(myContext)
   // Candidates to run in parallel
   .beginWith(listOf(plantName1, plantName2, plantName3))
   // Dependent work (only runs after all previous work in chain)
   .then(cache)
   .then(upload)
   // Call enqueue to kick things off
   .enqueue()

Java

WorkManager.getInstance(myContext)
   // Candidates to run in parallel
   .beginWith(Arrays.asList(plantName1, plantName2, plantName3))
   // Dependent work (only runs after all previous work in chain)
   .then(cache)
   .then(upload)
   // Call enqueue to kick things off
   .enqueue();

Combinadores de entradas

Cuando encadenas instancias de OneTimeWorkRequest, el resultado de las solicitudes de trabajo superiores se pasa como entrada a las secundarias. En el ejemplo anterior, las salidas de plantName1, plantName2 y plantName3 se pasarían como entradas a la solicitud de cache.

Para administrar las entradas de varias solicitudes de trabajos superiores, WorkManager usa InputMerger.

WorkManager proporciona dos tipos diferentes de InputMerger:

  • OverwritingInputMerger trata de agregar todas las claves de todas las entradas en el resultado. En caso de conflictos, reemplaza las claves definidas anteriormente.

  • ArrayCreatingInputMerger trata de combinar las entradas y crea arreglos cada vez que sea necesario.

Si tienes algún caso de uso más específico, puedes escribir tu propio combinador y subclasificar InputMerger.

OverwriteInputMerger

OverwritingInputMerger es el método de combinación predeterminado. Si hay conflictos de claves en la combinación, el valor más reciente de una clave reemplazará cualquier versión anterior en los datos de salida que se generen.

Por ejemplo, si las entradas de plantas tienen una clave que coincide con sus respectivos nombres de variables ("plantName1", "plantName2" y "plantName3"), los datos que se pasan al trabajador cache tendrán tres pares clave-valor.

Diagrama que muestra tres trabajos que pasan resultados diferentes al siguiente trabajo en la cadena. Como los tres resultados tienen claves diferentes, el trabajo siguiente recibirá tres pares clave-valor.

Si hay un conflicto, el último trabajador en finalizar será el "ganador", por lo que su valor se pasará a cache.

Diagrama que muestra tres trabajos que pasan resultados al siguiente trabajo en la cadena. En este caso, dos de esos trabajos generan resultados con la misma clave. Por lo tanto, el siguiente trabajo recibirá dos pares clave-valor y descartará uno de los resultados en conflicto.

Dado que las solicitudes de trabajo se ejecutan en paralelo, no se puede garantizar el orden exacto en el que se ejecutarán. En el ejemplo anterior, plantName1 podría contener el valor de "tulip" o "elm" según el último que se escriba. En el caso de que exista la posibilidad de generarse un conflicto de claves y necesites conservar todos los datos de salida en una combinación, ArrayCreatingInputMerger podría resultar más adecuado.

ArrayCreatingInputMerger

Para el ejemplo anterior, dado que deseamos preservar los resultados de todos los trabajadores de nombres de plantas, deberíamos usar un objeto ArrayCreatingInputMerger.

Kotlin

val cache: OneTimeWorkRequest = OneTimeWorkRequestBuilder<PlantWorker>()
   .setInputMerger(ArrayCreatingInputMerger::class)
   .setConstraints(constraints)
   .build()

Java

OneTimeWorkRequest cache = new OneTimeWorkRequest.Builder(PlantWorker.class)
       .setInputMerger(ArrayCreatingInputMerger.class)
       .setConstraints(constraints)
       .build();

ArrayCreatingInputMerger vincula cada clave con un arreglo. Si cada clave es única, se obtendrá como resultado una serie de arreglos de un solo elemento.

Diagrama que muestra tres trabajos que pasan resultados diferentes al siguiente trabajo en la cadena. En el siguiente trabajo, se pasan tres arreglos, uno para cada clave del resultado. Cada arreglo tiene un solo miembro.

Si hay una colisión de claves, los valores correspondientes se agrupan en un arreglo.

Diagrama que muestra tres trabajos que pasan resultados al siguiente trabajo en la cadena. En este caso, dos de esos trabajos generan resultados con la misma clave. En el siguiente trabajo, se pasan dos arreglos, uno para cada clave. Dado que hubo dos resultados con esa clave, uno de los arreglos tiene dos miembros.

Estados de trabajo y encadenado

Las cadenas de OneTimeWorkRequest se ejecutan de forma secuencial si el trabajo se completa correctamente (es decir, muestran Result.success()). Mientras se ejecutan las solicitudes de trabajo, estas podrían cancelarse o se podrían producir errores, lo que afectaría las solicitudes dependientes que siguen.

Cuando el primer elemento OneTimeWorkRequest se pone en cola en una cadena de solicitudes de trabajo, se bloquean todas las solicitudes posteriores hasta que se completa la primera.

Diagrama que muestra una cadena de trabajos. Se pone en cola el primer trabajo; todos los trabajos posteriores se bloquean hasta que se termine de ejecutar el primero.

Una vez que esté en cola y se cumplan todas las restricciones de trabajo, se comenzará a ejecutar la primera solicitud. Si el trabajo se completa correctamente en el elemento raíz OneTimeWorkRequest o List<OneTimeWorkRequest> (es decir que muestra Result.success()), se pondrá en cola el siguiente conjunto de solicitudes de trabajo dependientes.

Diagrama que muestra una cadena de trabajos. El primer trabajo se completó de forma correcta, y los dos que le siguen se ponen en cola. Los trabajos restantes quedan bloqueados hasta que finalicen los que los anteceden.

Mientras cada solicitud de trabajo se complete correctamente, este mismo patrón se propagará al resto de la cadena hasta que se completen todos los trabajos. Si bien esta es la manera más sencilla y de mayor preferencia, también es importante controlar los estados de error.

Cuando se produce un error mientras un trabajador está procesando tu solicitud, puedes volver a intentarlo según la política de retirada que definas. Si reintentas una solicitud que forma parte de una cadena, solo se volverá a intentar ejecutar esa solicitud con los datos de entrada proporcionados. No se verá afectado ningún trabajo que se ejecute en paralelo.

Diagrama que muestra una cadena de trabajos. Se produjo un error en uno de los trabajos, pero tenía configurada una política de retirada. Ese trabajo se volverá a ejecutar luego de que haya transcurrido la cantidad de tiempo necesaria. Se bloquearán los trabajos posteriores de la cadena hasta que se ejecute correctamente.

A fin de obtener más información para definir estrategias de reintento personalizadas, consulta Política de retirada y reintento.

Si esa política de reintento no está definida o se agotó, o bien si alcanzas algún estado en el que un elemento OneTimeWorkRequest muestra Result.failure(), esa solicitud de trabajo y las demás solicitudes dependientes se marcarán como FAILED.

Diagrama que muestra una cadena de trabajos. Se produjo un error en un trabajo y no se puede reintentar. En consecuencia, fallarán todos los trabajos posteriores de la cadena.

Se aplica la misma lógica cuando se cancela un elemento OneTimeWorkRequest. Las solicitudes de trabajo dependientes también se marcan como CANCELLED, por lo que no se ejecutarán sus trabajos.

Diagrama que muestra una cadena de trabajos. Se canceló un trabajo. En consecuencia, se cancelan todos los trabajos posteriores de la cadena.

Ten en cuenta que, si agregas más solicitudes de trabajo a una cadena con solicitudes fallidas o canceladas, las nuevas solicitudes también se marcarán como FAILED o CANCELLED, respectivamente. Si quieres extender el trabajo de una cadena existente, consulta APPEND_OR_REPLACE en ExistingWorkPolicy.

Cuando creas cadenas de solicitudes de trabajo, debes definir políticas de reintento en las solicitudes dependientes para asegurarte de que el trabajo se complete siempre de manera oportuna. Las solicitudes de trabajo fallidas podrían generar cadenas incompletas o estados inesperados.

Para obtener más información, consulta Cómo cancelar y detener el trabajo.