O WorkManager facilita a configuração e a programação de solicitações de tarefas elaboradas. Você pode usar as APIs para casos como estes:
- Sequências encadeadas de tarefas que são executadas em uma ordem específica
- Sequências com nomes exclusivos, com regras para o que acontecerá se o app iniciar duas sequências com o mesmo nome
- Tarefas que transmitem e retornam valores, incluindo tarefas encadeadas em que cada tarefa envia argumentos para a tarefa seguinte na cadeia
Tarefas encadeadas
É possível que seu app tenha que executar diversas tarefas em uma ordem específica.
O WorkManager
permite que você crie e coloque em fila uma sequência de trabalhos que
especifica diversas tarefas e a ordem em que elas serão executadas.
Por exemplo, suponha que seu app tenha três
objetos OneTimeWorkRequest
: workA
, workB
e
workC
. As tarefas precisam ser executadas nessa ordem. Para colocá-las em fila, crie uma sequência
com o método
WorkManager.beginWith(OneTimeWorkRequest)
,
transmitindo o primeiro objeto OneTimeWorkRequest
.
Esse método retorna um objeto WorkContinuation
, que
definirá a sequência de tarefas. Em seguida, adicione
os objetos OneTimeWorkRequest
restantes na ordem correta com
WorkContinuation.then(OneTimeWorkRequest)
e,
por fim, coloque a sequência toda em fila com
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();
O WorkManager
executa as tarefas na ordem solicitada,
de acordo com as restrições especificadas de cada tarefa. Se alguma tarefa retornar
Result.failure()
,
a sequência inteira será encerrada.
Você também pode transmitir vários objetos OneTimeWorkRequest
para qualquer chamada
beginWith(List<OneTimeWorkRequest>)
e
then(List<OneTimeWorkRequest>)
. Se você transmitir
vários objetos OneTimeWorkRequest
para uma única chamada de
método, o WorkManager
executará todas essas tarefas (em paralelo)
antes de executar o resto da sequência. Exemplo:
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();
Você pode criar sequências mais complexas mesclando várias cadeias com os métodos WorkContinuation.combine(List<OneTimeWorkRequest>)
. Por exemplo, suponha que você queira executar uma sequência como esta:
WorkContinuation
para configurar tarefas encadeadas complexas.
Para configurar essa sequência, crie duas cadeias separadas e mescle-as em uma terceira:
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();
Nesse caso, o WorkManager
executa workA
antes de workB
. Ele também
executa workC
antes de workD
. Quando workB
e workD
estiverem concluídos,
o WorkManager
executará workE
.
Há diversas variantes dos métodos WorkContinuation
que oferecem abreviações para situações específicas. Para ver mais detalhes, consulte a
referência de WorkContinuation
.
Sequências de trabalho únicas
Você pode criar uma sequência de trabalhos exclusivos iniciando a sequência com uma
chamada para
beginUniqueWork(String, ExistingWorkPolicy, OneTimeWorkRequest)
em vez de beginWith(OneTimeWorkRequest)
.
Cada sequência de trabalhos exclusivos tem um nome. O WorkManager
só
permite uma sequência de trabalhos com o mesmo nome por vez. Ao criar uma nova sequência de
trabalhos exclusivos, especifique o que o WorkManager
fará se
existir uma sequência não concluída com o mesmo nome:
- Cancelar a sequência existente e usar REPLACE para substituí-la por outra nova.
- Usar KEEP para manter a sequência existente e ignorar a nova solicitação.
- Usar APPEND para anexar a nova sequência à existente e executar a primeira tarefa da nova sequência depois que a última tarefa da sequência existente terminar.
Sequências de trabalhos exclusivos podem ser úteis caso você tenha uma tarefa que não possa ser colocada em fila várias vezes. Por exemplo, se o app precisar sincronizar dados com a rede, você poderá colocar uma sequência com o nome "sync" na fila e especificar que a nova tarefa será ignorada se existir uma sequência com esse nome. Sequências de trabalhos exclusivos também podem ser úteis caso você precise aumentar gradualmente uma longa cadeia de tarefas. Por exemplo, um app de edição de fotos pode permitir que os usuários desfaçam uma longa cadeia de ações. Cada uma dessas operações de desfazer uma ação pode demorar um pouco, mas elas precisam ser realizadas na ordem correta. Nesse caso, o app pode criar uma cadeia de "desfazer" e anexar cada operação de desfazer à cadeia, conforme necessário.
Parâmetros de entrada e valores retornados
Para maior flexibilidade, você pode transmitir argumentos às suas tarefas e fazer com que elas
retornem resultados. Os valores passados e retornados são pares de chave-valor. Para transmitir um
argumento para uma tarefa, chame o método
WorkRequest.Builder.setInputData(Data)
antes de criar o objeto
WorkRequest
. Esse método usa um objeto Data
, que é criado com
Data.Builder
. A classe Worker
pode acessar esses
argumentos chamando
Worker.getInputData()
. Para
gerar um valor de retorno, a tarefa precisa incluí-lo no Result
(por exemplo, retornando
Result.success(Data)
).
Você pode conseguir o resultado observando a WorkInfo
da tarefa.
Por exemplo, suponha que você tenha uma classe Worker
que realiza um
cálculo muito demorado. O código a seguir mostra como seria a classe
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 criar o trabalho e passar os argumentos, você usaria um código assim:
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);
O valor retornado seria disponibilizado na WorkInfo
da tarefa:
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 ... } });
Se você encadear tarefas, os resultados de uma tarefa serão disponibilizados como entradas para a
tarefa seguinte na cadeia. No caso de uma cadeia simples, com apenas um
OneTimeWorkRequest
seguida por outro
OneTimeWorkRequest
, a primeira tarefa retorna o resultado
chamando
Result.success(Data)
e a tarefa seguinte busca esse resultado chamando
getInputData()
. Se a
cadeia for mais complexa (por exemplo, porque várias tarefas enviam resultados para
uma única tarefa seguinte), defina um InputMerger
no OneTimeWorkRequest.Builder
para
especificar o que acontecerá se diferentes tarefas retornarem um resultado com a mesma
chave.
Outros recursos
Para saber mais sobre o WorkManager, consulte os recursos listados a seguir.
Amostras
- App de amostra WorkManager (em inglês)
- Sunflower, um app de jardinagem que ilustra as práticas recomendadas de desenvolvimento Android com o Android Jetpack.