WorkManager facilita los procesos de configuración y programación de solicitudes de tareas complejas. Puedes usar las API para situaciones como las siguientes:
- Secuencias encadenadas de tareas que se ejecutan en un orden especificado
- Secuencias de nombres únicas, con reglas para lo que sucede si la app inicia dos secuencias con el mismo nombre
- Tareas que pasan y muestran valores, incluidas las tareas encadenadas en las que cada elemento pasa argumentos a la siguiente tarea de la cadena
Tareas encadenadas
Es posible que tu app deba ejecutar varias tareas en un orden específico.
WorkManager
te permite crear y poner en cola una secuencia de trabajo que especifica varias tareas y en qué orden deben ejecutarse.
Por ejemplo, supongamos que tu app tiene tres objetos OneTimeWorkRequest
: workA
, workB
y workC
. Las tareas deben ejecutarse en ese orden. Para ponerlos en cola, crea una secuencia con el método WorkManager.beginWith(OneTimeWorkRequest)
y pasa el primer objeto OneTimeWorkRequest
. Ese método mostrará un objeto WorkContinuation
, que definirá una secuencia de tareas. Luego, agrega los objetos OneTimeWorkRequest
restantes, en orden, con WorkContinuation.then(OneTimeWorkRequest)
y, finalmente, pon en cola toda la secuencia con WorkContinuation.enqueue()
:
Kotlin
WorkManager.getInstance(myContext) .beginWith(workA) // Note: WorkManager.beginWith() returns a // WorkContinuation object; the following calls are // to WorkContinuation methods .then(workB) // FYI, then() returns a new WorkContinuation instance .then(workC) .enqueue()
Java
WorkManager.getInstance(myContext) .beginWith(workA) // Note: WorkManager.beginWith() returns a // WorkContinuation object; the following calls are // to WorkContinuation methods .then(workB) // FYI, then() returns a new WorkContinuation instance .then(workC) .enqueue();
WorkManager
ejecuta las tareas en el orden solicitado, según las restricciones especificadas de cada tarea. Si alguna tarea muestra Result.failure()
, finaliza toda la secuencia.
También puedes enviar varios objetos OneTimeWorkRequest
a cualquiera de las llamadas a beginWith(List<OneTimeWorkRequest>)
y then(List<OneTimeWorkRequest>)
. Si pasas varios objetos OneTimeWorkRequest
a una sola llamada de método, WorkManager
ejecutará todas esas tareas (en paralelo) antes de ejecutar el resto de la secuencia. Por ejemplo:
Kotlin
WorkManager.getInstance(myContext) // First, run all the A tasks (in parallel): .beginWith(Arrays.asList(workA1, workA2, workA3)) // ...when all A tasks are finished, run the single B task: .then(workB) // ...then run the C tasks (in parallel): .then(Arrays.asList(workC1, workC2)) .enqueue()
Java
WorkManager.getInstance(myContext) // First, run all the A tasks (in parallel): .beginWith(Arrays.asList(workA1, workA2, workA3)) // ...when all A tasks are finished, run the single B task: .then(workB) // ...then run the C tasks (in parallel): .then(Arrays.asList(workC1, workC2)) .enqueue();
Puedes crear secuencias más complejas uniendo varias cadenas con los métodos WorkContinuation.combine(List<OneTimeWorkRequest>)
. Por ejemplo, supongamos que quieres ejecutar una secuencia como la siguiente:
WorkContinuation
para configurar tareas en cadena complejas
Para configurar esta secuencia, crea dos cadenas separadas y únelas a una tercera:
Kotlin
val chain1 = WorkManager.getInstance(myContext) .beginWith(workA) .then(workB) val chain2 = WorkManager.getInstance(myContext) .beginWith(workC) .then(workD) val chain3 = WorkContinuation .combine(Arrays.asList(chain1, chain2)) .then(workE) chain3.enqueue()
Java
WorkContinuation chain1 = WorkManager.getInstance(myContext) .beginWith(workA) .then(workB); WorkContinuation chain2 = WorkManager.getInstance(myContext) .beginWith(workC) .then(workD); WorkContinuation chain3 = WorkContinuation .combine(Arrays.asList(chain1, chain2)) .then(workE); chain3.enqueue();
En este caso, WorkManager
ejecuta workA
antes que workB
. También ejecuta workC
antes que workD
. Después de que hayan terminado workB
y workD
, WorkManager
ejecuta workE
.
Hay una serie de variantes de los métodos WorkContinuation
que proporcionan versiones abreviadas para situaciones específicas. Para obtener más información, consulta la referencia WorkContinuation
.
Secuencias de trabajo únicas
Para crear una secuencia de trabajo única, inicia la secuencia con una llamada a beginUniqueWork(String, ExistingWorkPolicy, OneTimeWorkRequest)
en lugar de a beginWith(OneTimeWorkRequest)
.
Cada secuencia de trabajo única tiene un nombre. WorkManager
solo permite una secuencia de trabajo con ese nombre a la vez. Cuando creas una nueva secuencia de trabajo única, especificas lo que debe hacer WorkManager
si ya hay una secuencia sin terminar con el mismo nombre:
- Cancela la secuencia existente y la REEMPLAZA con la nueva.
- MANTIENE la secuencia existente e ignora la solicitud nueva.
- ADJUNTA la nueva secuencia a la existente. De esa manera, se ejecutará la primera tarea de la nueva secuencia después de que finalice la última tarea de la secuencia existente.
Las secuencias de trabajo únicas pueden ser útiles si tienes una tarea que no se debe poner en cola varias veces. Por ejemplo, si tu app necesita sincronizar sus datos con la red, puedes poner en cola una secuencia llamada "sincronización" y especificar que tu nueva tarea debería ignorarse si ya hay una secuencia con ese nombre. Las secuencias de trabajo único también pueden ser útiles si necesitas crear gradualmente una cadena larga de tareas. Por ejemplo, una app de edición de fotos puede permitir que los usuarios deshagan una cadena larga de acciones. Cada una de esas operaciones de deshacer puede llevar un tiempo, pero deben realizarse en el orden correcto. En este caso, la app podría crear una cadena de "deshacer" y agregar cada operación de deshacer a la cadena según sea necesario.
Parámetros de entrada y valores mostrados
Para mayor flexibilidad, puedes pasar argumentos a tus tareas y hacer que las tareas muestren resultados. Los valores pasados y mostrados son pares clave-valor. Para pasar un argumento a una tarea, llama al método WorkRequest.Builder.setInputData(Data)
antes de crear el objeto WorkRequest
. Ese método toma un objeto Data
, que creas con Data.Builder
. La clase Worker
puede acceder a esos argumentos llamando a Worker.getInputData()
. A fin de obtener un valor para mostrar, la tarea debe incluirlo en Result
(por ejemplo, mostrando Result.success(Data)
).
Puedes obtener el resultado observando la tarea WorkInfo
.
Por ejemplo, supongamos que tienes una clase Worker
que realiza un cálculo lento. En el siguiente fragmento de código, se muestra cómo se vería la clase Worker
:
Kotlin
// Define the parameter keys: const val KEY_X_ARG = "X" const val KEY_Y_ARG = "Y" const val KEY_Z_ARG = "Z" // ...and the result key: const val KEY_RESULT = "result" // Define the Worker class: class MathWorker(context : Context, params : WorkerParameters) : Worker(context, params) { override fun doWork(): Result { val x = inputData.getInt(KEY_X_ARG, 0) val y = inputData.getInt(KEY_Y_ARG, 0) val z = inputData.getInt(KEY_Z_ARG, 0) // ...do the math... val result = myLongCalculation(x, y, z); //...set the output, and we're done! val output: Data = workDataOf(KEY_RESULT to result) return Result.success(output) } }
Java
// Define the Worker class: public class MathWorker extends Worker { // Define the parameter keys: public static final String KEY_X_ARG = "X"; public static final String KEY_Y_ARG = "Y"; public static final String KEY_Z_ARG = "Z"; // ...and the result key: public static final String KEY_RESULT = "result"; public MathWorker( @NonNull Context context, @NonNull WorkerParameters params) { super(context, params); } @Override public Result doWork() { // Fetch the arguments (and specify default values): int x = getInputData().getInt(KEY_X_ARG, 0); int y = getInputData().getInt(KEY_Y_ARG, 0); int z = getInputData().getInt(KEY_Z_ARG, 0); // ...do the math... int result = myLongCalculation(x, y, z); //...set the output, and we're done! Data output = new Data.Builder() .putInt(KEY_RESULT, result) .build(); return Result.success(output); } }
Para crear el trabajo y pasar los argumentos, debes usar código como el siguiente:
Kotlin
val myData: Data = workDataOf("KEY_X_ARG" to 42, "KEY_Y_ARG" to 421, "KEY_Z_ARG" to 8675309) // ...then create and enqueue a OneTimeWorkRequest that uses those arguments val mathWork = OneTimeWorkRequestBuilder<MathWorker>() .setInputData(myData) .build() WorkManager.getInstance(myContext).enqueue(mathWork)
Java
// Create the Data object: Data myData = new Data.Builder() // We need to pass three integers: X, Y, and Z .putInt(KEY_X_ARG, 42) .putInt(KEY_Y_ARG, 421) .putInt(KEY_Z_ARG, 8675309) // ... and build the actual Data object: .build(); // ...then create and enqueue a OneTimeWorkRequest that uses those arguments OneTimeWorkRequest mathWork = new OneTimeWorkRequest.Builder(MathWorker.class) .setInputData(myData) .build(); WorkManager.getInstance(myContext).enqueue(mathWork);
El valor que se muestre estará disponible en la tarea WorkInfo
:
Kotlin
WorkManager.getInstance(myContext).getWorkInfoByIdLiveData(mathWork.id) .observe(this, Observer { info -> if (info != null && info.state.isFinished) { val myResult = info.outputData.getInt(KEY_RESULT, myDefaultValue) // ... do something with the result ... } })
Java
WorkManager.getInstance(myContext).getWorkInfoByIdLiveData(mathWork.getId()) .observe(lifecycleOwner, info -> { if (info != null && info.getState().isFinished()) { int myResult = info.getOutputData().getInt(KEY_RESULT, myDefaultValue)); // ... do something with the result ... } });
Si encadenas tareas, los resultados de una tarea estarán disponibles como entradas para la siguiente tarea de la cadena. Si es una cadena simple, con un solo OneTimeWorkRequest
seguido de otro sencillo OneTimeWorkRequest
, la primera tarea mostrará su resultado llamando a Result.success(Data)
, y la siguiente tarea recuperará el resultado mediante una llamada a getInputData()
. Si la cadena es más complicada, por ejemplo, debido a que varias tareas envían resultados a una sola tarea siguiente, puedes definir un InputMerger
en OneTimeWorkRequest.Builder
para especificar qué debe ocurrir si diferentes tareas muestran un resultado con la misma clave.
Recursos adicionales
Para obtener más información sobre WorkManager, consulta los siguientes recursos adicionales.
Ejemplos
- App de muestra de WorkManager
- Sunflower, una app de jardinería que ilustra las prácticas recomendadas de desarrollo de Android con Android Jetpack