Dopo aver definito Worker
e WorkRequest
, l'ultimo passaggio consiste nell'accodare il lavoro. Il modo più semplice per accodare il lavoro
consiste nel chiamare il metodo enqueue()
di WorkManager, passando il valore 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);
Fai attenzione quando metti in coda i lavori per evitare duplicati. Ad esempio, un'app potrebbe provare a caricare i log in un servizio di backend ogni 24 ore. Se non fai 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 programmare il lavoro come lavoro unico.
Lavoro unico
Il lavoro unico è un concetto potente che ti garantisce di avere 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 univoci sono associati a una sola istanza del lavoro.
Il lavoro unico può essere applicato sia a lavori una tantum che a lavori periodici. Puoi creare una sequenza di lavoro univoca chiamando uno di questi metodi, a seconda che tu stia pianificando un lavoro ricorrente o un lavoro una tantum.
WorkManager.enqueueUniqueWork()
per lavoro una tantumWorkManager.enqueueUniquePeriodicWork()
per attività periodiche
Entrambi i metodi accettano 3 argomenti:
- uniqueWorkName: un
String
utilizzato per identificare in modo univoco la richiesta di lavoro. - existingWorkPolicy: un
enum
che indica a WorkManager cosa fare se esiste già una catena di lavoro non completata con quel nome univoco. Per ulteriori informazioni, consulta le norme per la risoluzione dei conflitti. - work: il
WorkRequest
per programmare.
Utilizzando un lavoro unico, possiamo risolvere il problema di programmazione duplicato indicato 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 conservato e non viene aggiunto nessun nuovo job.
Le sequenze di lavoro univoche possono essere utili anche se devi creare gradualmente una lunga catena di attività. Ad esempio, un'app di fotoritocco potrebbe consentire all'utente 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, vedi Il lavoro di Caino.
Norme per la risoluzione dei conflitti
Quando pianifichi un lavoro univoco, devi indicare a WorkManager quale azione eseguire in caso di conflitto. Per farlo, passa un'enumerazione quando metti in coda il lavoro.
Per i lavori una tantum devi fornire un elemento ExistingWorkPolicy
, che supporta quattro opzioni per la gestione del conflitto.
REPLACE
lavori esistenti con il nuovo lavoro. Questa opzione annulla il lavoro esistente.KEEP
lavoro esistente e ignora quello nuovo.APPEND
il nuovo lavoro alla fine di quello esistente. Questo criterio farà sì che il nuovo lavoro venga connesso a quello esistente, in esecuzione al termine di quello esistente.
Il lavoro esistente diventa un prerequisito della nuova opera. Se il lavoro esistente
diventa CANCELLED
o FAILED
, anche la nuova opera sarà CANCELLED
o FAILED
.
Se vuoi che il nuovo lavoro venga eseguito a prescindere dallo stato di quello esistente, utilizza invece APPEND_OR_REPLACE
.
APPEND_OR_REPLACE
funziona in modo simile aAPPEND
, con la differenza che non dipende dallo stato di lavoro dei prerequisiti. Se il lavoro esistente èCANCELLED
oFAILED
, il nuovo lavoro continuerà a essere eseguito.
Per i lavori del ciclo mestruale, fornisci un elemento ExistingPeriodicWorkPolicy
, che supporta due opzioni: REPLACE
e KEEP
. Queste opzioni funzionano allo stesso modo
delle controparti esistentiWorkPolicy.
Osservare il tuo lavoro
In qualsiasi momento dopo aver accodato il lavoro, puoi controllarne lo stato eseguendo una query su WorkManager tramite il name
, id
o 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 elemento ListenableFuture
di un oggetto WorkInfo
, che include id
dell'opera, i relativi tag, State
corrente e qualsiasi set di dati di output tramite Result.success(outputData)
.
Una variante LiveData
di ciascuno dei metodi ti consente di osservare le modifiche al WorkInfo
registrando un listener. Ad esempio, se vuoi mostrare un messaggio all'utente al termine di un lavoro, potresti impostarlo come segue:
Kotlin
workManager.getWorkInfoByIdLiveData(syncWorker.id) .observe(viewLifecycleOwner) { 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 in coda utilizzando oggetti WorkQuery
. WorkQuery supporta l'esecuzione di query per il lavoro in base a una combinazione dei suoi tag, stato e nome dell'opera univoco.
Il seguente esempio mostra come trovare tutte le operazioni con il tag "syncTag", che si trova in stato FAILED
o CANCELLED
e ha un nome univoco dell'opera "preProcess" o "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 elemento WorkQuery
è AND
-ed con gli altri. Ciascun valore in un componente è OR
-ed. Ad esempio: (name1 OR name2
OR ...) AND (tag1 OR tag2 OR ...) AND (state1 OR state2 OR ...)
.
WorkQuery
funziona anche con l'equivalente LiveData,
getWorkInfosLiveData()
.
Annullamento e interruzione del lavoro
Se non hai più bisogno di eseguire il lavoro precedentemente aggiunto in coda, puoi richiederne
l'annullamento. Un lavoro può essere annullato tramite name
, id
o 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");
In background, WorkManager controlla State
del lavoro. Se un lavoro è già completato, non succede nulla. In caso contrario, lo stato dell'opera viene modificato in CANCELLED
e il lavoro non verrà eseguito in futuro. Anche tutti i job di WorkRequest
dipendenti da questo lavoro saranno CANCELLED
.
Al momento, il lavoro di RUNNING
riceve una chiamata al numero
ListenableWorker.onStopped()
.
Esegui l'override di questo metodo per gestire qualsiasi possibile pulizia. Vedi Arresta un worker in esecuzione per ulteriori informazioni.
Arresta un worker in esecuzione
Esistono diversi motivi per cui l'esecuzione di Worker
potrebbe essere interrotta da WorkManager:
- Hai chiesto esplicitamente l'annullamento (ad esempio chiamando il numero
WorkManager.cancelWorkById(UUID)
). - Nel caso di operazioni uniche, hai esplicitamente inserito in coda un nuovo
WorkRequest
conExistingWorkPolicy
REPLACE
. Il vecchioWorkRequest
viene immediatamente considerato annullato. - I vincoli del tuo lavoro non vengono più soddisfatti.
- Il sistema ha indicato all'app di interrompere il lavoro per un motivo non determinato. Ciò può verificarsi se superi la scadenza di esecuzione di 10 minuti. Il lavoro è programmato per un secondo momento.
In queste condizioni, il worker è stato arrestato.
Devi interrompere cooperativamente il lavoro in corso e rilasciare tutte le risorse a cui il worker sta lavorando. Ad esempio, a questo punto devi chiudere gli handle aperti per i database e i file. Esistono due meccanismi a tua disposizione per capire quando il lavoratore si ferma.
Callback onSST()
WorkManager richiama
ListenableWorker.onStopped()
non appena il worker è stato arrestato. Esegui l'override di questo metodo per chiudere
tutte le risorse che stai conservando.
proprietà isSST()
Puoi chiamare il metodo
ListenableWorker.isStopped()
per verificare se il lavoratore si è già arrestato. Se esegui operazioni a lunga esecuzione o ripetitive nel worker, dovresti controllare spesso questa proprietà e utilizzarla come indicatore per interrompere il lavoro il prima possibile.
Nota: WorkManager ignora
Result
impostato da un worker
che ha ricevuto l'indicatore onStop, perché il worker è già considerato
arresto.