Dopo aver definito
Worker e
WorkRequest,
l'ultimo passaggio consiste nell'accodare il lavoro. Il modo più semplice per accodare il lavoro è chiamare il metodo enqueue() di WorkManager, passando il WorkRequest che vuoi eseguire.
Kotlin
val myWork: WorkRequest = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork)
Java
WorkRequest myWork = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork);
Presta attenzione quando accodi il lavoro per evitare duplicati. Ad esempio, un'app potrebbe provare a caricare i log in un servizio di backend ogni 24 ore. Se non presti attenzione, potresti finire per accodare la stessa attività molte volte, anche se il job deve essere eseguito una sola volta. Per raggiungere questo obiettivo, puoi pianificare il lavoro come lavoro unico.
Lavoro unico
Il lavoro unico è un concetto potente che garantisce che sia presente una sola istanza di lavoro con un determinato nome alla volta. A differenza degli ID, i nomi univoci sono leggibili e specificati dallo sviluppatore anziché essere generati automaticamente da WorkManager. A differenza dei tag, i nomi unici sono associati a una sola istanza di lavoro.
Il lavoro unico può essere applicato sia al lavoro una tantum sia al lavoro periodico. Puoi creare una sequenza di lavoro unica chiamando uno di questi metodi, a seconda che tu stia pianificando un lavoro ripetuto o un lavoro una tantum.
WorkManager.enqueueUniqueWork()per il lavoro una tantumWorkManager.enqueueUniquePeriodicWork()per il lavoro periodico
Entrambi questi metodi accettano 3 argomenti:
- uniqueWorkName - Un
Stringutilizzato per identificare in modo univoco la richiesta di lavoro. - existingWorkPolicy - Un
enumche indica a WorkManager cosa fare se esiste già una catena di lavoro non terminata con quel nome univoco. Per saperne di più, consulta le norme per la risoluzione dei conflitti. - work - il
WorkRequestda pianificare.
Utilizzando il lavoro unico, possiamo risolvere il problema di pianificazione duplicata notato in precedenza.
Kotlin
val sendLogsWorkRequest =
PeriodicWorkRequestBuilder<SendLogsWorker>(24, TimeUnit.HOURS)
.setConstraints(Constraints.Builder()
.setRequiresCharging(true)
.build()
)
.build()
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
"sendLogs",
ExistingPeriodicWorkPolicy.KEEP,
sendLogsWorkRequest
)
Java
PeriodicWorkRequest sendLogsWorkRequest = new
PeriodicWorkRequest.Builder(SendLogsWorker.class, 24, TimeUnit.HOURS)
.setConstraints(new Constraints.Builder()
.setRequiresCharging(true)
.build()
)
.build();
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
"sendLogs",
ExistingPeriodicWorkPolicy.KEEP,
sendLogsWorkRequest);
Ora, se il codice viene eseguito mentre un job sendLogs è già in coda, il job esistente viene mantenuto e non viene aggiunto un nuovo job.
Le sequenze di lavoro uniche possono essere utili anche se devi creare gradualmente una lunga catena di attività. Ad esempio, un'app di fotoritocco potrebbe consentire agli utenti di annullare una lunga catena di azioni. Ognuna di queste operazioni di annullamento potrebbe richiedere un po' di tempo, ma deve essere eseguita nell'ordine corretto. In questo caso, l'app potrebbe creare una catena di "annullamento" e aggiungere ogni operazione di annullamento alla catena in base alle esigenze. Per maggiori dettagli, consulta la sezione Concatenare il lavoro.
Norme per la risoluzione dei conflitti
Quando pianifichi un lavoro unico, devi indicare a WorkManager l'azione da intraprendere in caso di conflitto. A questo scopo, passa un enum quando accodi il lavoro.
Per il lavoro una tantum, fornisci un
ExistingWorkPolicy, che
supporta 4 opzioni per la gestione del conflitto.
REPLACE: sostituisce il lavoro esistente con il nuovo lavoro. Questa opzione annulla il lavoro esistente.KEEP: mantiene il lavoro esistente e ignora il nuovo lavoro.APPEND: aggiunge il nuovo lavoro alla fine del lavoro esistente. Questa norma farà sì che il nuovo lavoro venga concatenato al lavoro esistente, eseguito dopo il completamento del lavoro esistente.
Il lavoro esistente diventa un prerequisito per il nuovo lavoro. Se il lavoro esistente diventa CANCELLED o FAILED, anche il nuovo lavoro diventa CANCELLED o FAILED.
Se vuoi che il nuovo lavoro venga eseguito indipendentemente dallo stato del lavoro esistente, utilizza APPEND_OR_REPLACE.
APPEND_OR_REPLACEfunziona in modo simile aAPPEND, ma non dipende dallo stato del lavoro prerequisito. Se il lavoro esistente èCANCELLEDoFAILED, il nuovo lavoro viene comunque eseguito.
Per il lavoro periodico, fornisci un
ExistingPeriodicWorkPolicy,
che supporta 2 opzioni: REPLACE e KEEP. Queste opzioni funzionano come le rispettive controparti di ExistingWorkPolicy.
Osservare il lavoro
In qualsiasi momento dopo l'accodamento del lavoro, puoi verificarne lo stato eseguendo una query su WorkManager in base al name, all'id o a un tag associato.
Kotlin
// by id
workManager.getWorkInfoById(syncWorker.id) // ListenableFuture<WorkInfo>
// by name
workManager.getWorkInfosForUniqueWork("sync") // ListenableFuture<List<WorkInfo>>
// by tag
workManager.getWorkInfosByTag("syncTag") // ListenableFuture<List<WorkInfo>>
Java
// by id
workManager.getWorkInfoById(syncWorker.id); // ListenableFuture<WorkInfo>
// by name
workManager.getWorkInfosForUniqueWork("sync"); // ListenableFuture<List<WorkInfo>>
// by tag
workManager.getWorkInfosByTag("syncTag"); // ListenableFuture<List<WorkInfo>>
La query restituisce un
ListenableFuture
di un WorkInfo oggetto, che include l'
id del lavoro, i relativi tag, il relativo
current State, e qualsiasi set di dati di output utilizzando
Result.success(outputData).
Le varianti LiveData e Flow
di ciascuno dei metodi consentono di osservare le modifiche apportate a
WorkInfo registrando un listener. Ad esempio, se vuoi visualizzare un messaggio per l'utente quando un lavoro viene completato correttamente, puoi configurarlo nel seguente modo:
Kotlin
workManager.getWorkInfoByIdFlow(syncWorker.id)
.collect{ workInfo ->
if(workInfo?.state == WorkInfo.State.SUCCEEDED) {
Snackbar.make(requireView(),
R.string.work_completed, Snackbar.LENGTH_SHORT)
.show()
}
}
Java
workManager.getWorkInfoByIdLiveData(syncWorker.id)
.observe(getViewLifecycleOwner(), workInfo -> {
if (workInfo.getState() != null &&
workInfo.getState() == WorkInfo.State.SUCCEEDED) {
Snackbar.make(requireView(),
R.string.work_completed, Snackbar.LENGTH_SHORT)
.show();
}
});
Query di lavoro complesse
WorkManager 2.4.0 e versioni successive supportano query complesse per i job accodati utilizzando
WorkQuery oggetti. WorkQuery supporta l'esecuzione di query per il lavoro in base a una combinazione di tag, stato e nome del lavoro univoco.
L'esempio seguente mostra come trovare tutto il lavoro con il tag, "syncTag",
nello stato FAILED o CANCELLED e con un nome di lavoro univoco
either "preProcess" or "sync".
Kotlin
val workQuery = WorkQuery.Builder
.fromTags(listOf("syncTag"))
.addStates(listOf(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
.addUniqueWorkNames(listOf("preProcess", "sync")
)
.build()
val workInfos: ListenableFuture<List<WorkInfo>> = workManager.getWorkInfos(workQuery)
Java
WorkQuery workQuery = WorkQuery.Builder
.fromTags(Arrays.asList("syncTag"))
.addStates(Arrays.asList(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
.addUniqueWorkNames(Arrays.asList("preProcess", "sync")
)
.build();
ListenableFuture<List<WorkInfo>> workInfos = workManager.getWorkInfos(workQuery);
Ogni componente (tag, stato o nome) in un WorkQuery viene combinato con gli altri tramite l'operatore AND. Ogni valore in un componente viene combinato tramite l'operatore OR. Ad esempio: (name1 OR name2
OR ...) AND (tag1 OR tag2 OR ...) AND (state1 OR state2 OR ...).
WorkQuery funziona anche con l'equivalente LiveData,
getWorkInfosLiveData(),
e l'equivalente Flow, getWorkInfosFlow().
Annullare e interrompere il lavoro
Se non hai più bisogno che il lavoro accodato in precedenza venga eseguito, puoi chiedere che venga annullato. Il lavoro può essere annullato in base al name, all'id o a un tag associato.
Kotlin
// by id
workManager.cancelWorkById(syncWorker.id)
// by name
workManager.cancelUniqueWork("sync")
// by tag
workManager.cancelAllWorkByTag("syncTag")
Java
// by id
workManager.cancelWorkById(syncWorker.id);
// by name
workManager.cancelUniqueWork("sync");
// by tag
workManager.cancelAllWorkByTag("syncTag");
Dietro le quinte, WorkManager controlla il
State del lavoro. Se il lavoro è
già terminato,
non succede nulla. In caso contrario, lo stato del lavoro viene modificato in
CANCELLED e il lavoro
non verrà eseguito in futuro. Anche tutti i job
WorkRequest che sono dipendenti
da questo lavoro verranno
anche CANCELLED.
RUNNING lavoro
riceve una chiamata a
ListenableWorker.onStopped().
Esegui l'override di questo metodo per gestire qualsiasi potenziale pulizia. Per saperne di più, consulta la sezione Interrompere un
worker in esecuzione.
Interrompere un worker in esecuzione
Esistono alcuni motivi per cui il Worker in esecuzione potrebbe essere interrotto da WorkManager:
- Hai chiesto esplicitamente che venga annullato (ad esempio chiamando
WorkManager.cancelWorkById(UUID)). - Nel caso di lavoro unico,
hai accodato esplicitamente un nuovo
WorkRequestcon unExistingWorkPolicydiREPLACE. Il vecchioWorkRequestviene immediatamente considerato annullato. - I vincoli del tuo lavoro non sono più soddisfatti.
- Il sistema ha chiesto alla tua app di interrompere il lavoro per qualche motivo. Questo può accadere se superi il termine di esecuzione di 10 minuti. Il lavoro è pianificato per un nuovo tentativo in un secondo momento.
In queste condizioni, il worker viene interrotto.
Devi interrompere in modo cooperativo qualsiasi lavoro in corso e rilasciare tutte le risorse a cui il worker è collegato. Ad esempio, a questo punto devi chiudere gli handle aperti a database e file. Sono disponibili due meccanismi per capire quando il worker si sta arrestando.
Callback onStopped()
WorkManager invokes
ListenableWorker.onStopped()
non appena il worker viene interrotto. Esegui l'override di questo metodo per chiudere tutte le risorse a cui potresti essere collegato.
Proprietà isStopped()
Puoi chiamare il
ListenableWorker.isStopped() metodo per verificare se il worker è già stato interrotto. Se esegui operazioni a lunga esecuzione o ripetitive nel worker, devi controllare spesso questa proprietà e utilizzarla come segnale per interrompere il lavoro il prima possibile.
Nota: WorkManager ignora il
Result impostato da un worker
che ha ricevuto il segnale onStop, perché il worker è già considerato
interrotto.