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

Temas avanzados de WorkManager

WorkManager facilita los procesos de configuración y programación de solicitudes de tareas complejas. Puedes usar las API para situaciones como las siguientes:

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:

Figura 1: Puedes usar 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