Gestione del lavoro

Una volta definita la tua Worker e WorkRequest, l'ultimo passaggio è accodare il lavoro. Il modo più semplice per accodare il lavoro è 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 accoda i lavori per evitare duplicati. Ad esempio, un'app potrebbe tentare di caricare e i propri log a un servizio di backend ogni 24 ore. Se non fai attenzione, potresti finiscono per accodare la stessa attività molte volte, anche se il lavoro deve solo vengono eseguiti una sola volta. Per raggiungere questo obiettivo, puoi pianificare il lavoro come lavoro unico.

Opera esclusiva

Il lavoro unico è un concetto potente che garantisce di avere solo uno istanza di lavoro con un nome specifico alla volta. A differenza degli ID, i nomi univoci sono leggibili da una persona e specificati dallo sviluppatore anziché essere generati automaticamente da WorkManager. Non mi piace tag, univoci sono associati a una sola istanza di lavoro.

I lavori unici possono essere applicati sia al lavoro una tantum sia a quello periodico. Puoi creare un una sequenza di lavoro univoca chiamando uno di questi metodi, a seconda che stai pianificando un lavoro ricorrente o un lavoro una tantum.

Entrambi i metodi accettano tre argomenti:

  • uniqueWorkName: un String utilizzato per identificare in modo univoco l'opera richiesta.
  • existingWorkPolicy: un enum che indica a WorkManager cosa fare se esiste già una catena di lavori non completata con quel nome univoco. Consulta norme relative alla risoluzione dei conflitti per ulteriori informazioni.
  • work: i WorkRequest da pianificare.

Utilizzando un lavoro unico, possiamo risolvere il problema di pianificazione duplicato di cui abbiamo parlato 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, lo stato di un job viene mantenuto e non ne viene aggiunto di nuovi.

Le sequenze di lavoro univoche possono essere utili anche se devi creare gradualmente un una lunga catena di attività. Ad esempio, un'app di fotoritocco potrebbe consentire agli utenti di annullare un una lunga catena di azioni. Ognuna di queste operazioni di annullamento potrebbe richiedere del tempo, ma devono essere eseguite nell'ordine corretto. In questo caso, l'app potrebbe crea un'operazione di annullamento e aggiungere alla catena ogni operazione di annullamento in base alle esigenze. Consulta la sezione Catena di lavori per ulteriori informazioni.

Norme per la risoluzione dei conflitti

Quando pianifichi un lavoro unico, devi indicare a WorkManager quale azione intraprendere quando se si verifica un conflitto. Per farlo, passi un'enumerazione quando accoda il lavoro.

Per i lavori una tantum, fornisci un ExistingWorkPolicy, che supporta 4 opzioni per la gestione del conflitto.

  • REPLACE esistente con il nuovo lavoro. Questa opzione annulla il lavoro esistente.
  • KEEP lavori esistenti e ignorare il nuovo lavoro.
  • APPEND il nuovo lavoro per la fine del lavoro esistente. Grazie a queste norme, il tuo nuovo lavoro verrà collegato al e il lavoro esistente, in esecuzione al termine di quello esistente.

Il lavoro esistente diventa un prerequisito per il nuovo lavoro. Se il lavoro esistente diventa CANCELLED o FAILED, il nuovo lavoro è anche CANCELLED o FAILED. Se vuoi che il nuovo lavoro venga eseguito a prescindere dallo stato di quello esistente, usa invece APPEND_OR_REPLACE.

  • APPEND_OR_REPLACE funziona in modo simile a APPEND, ad eccezione del fatto che non dipende da stato di lavoro prerequisito. Se l'opera esistente è CANCELLED o FAILED, il nuovo lavoro è ancora in esecuzione.

Per i lavori in stile, fornisci un ExistingPeriodicWorkPolicy, che supporta due opzioni, REPLACE e KEEP. Queste opzioni funzionano allo stesso modo come controparti CurrentWorkPolicy.

Osservare il tuo lavoro

In qualsiasi momento dopo aver accodato il lavoro, puoi verificarne lo stato eseguendo query WorkManager in base al rispettivo name, 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 oggetto WorkInfo, che include id dell'opera, i suoi tag, i suoi State corrente ed eventuali dati di output imposta tramite Result.success(outputData).

Una variante LiveData di ciascuno ti consente di osservare le modifiche a WorkInfo registrando un listener. Ad esempio, se vuoi mostrare un messaggio all'utente quando alcuni lavori vengono completati correttamente, puoi configurarlo nel seguente modo:

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 supporta query complesse per i lavori accodati utilizzando WorkQuery oggetti. WorkQuery supporta query per il lavoro in base a una combinazione di tag, stato e nome univoco dell'opera.

L'esempio seguente mostra come trovare tutte le operazioni con il tag "syncTag", nello stato FAILED o CANCELLED e con un nome di lavoro univoco "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 la classe altri. Ogni 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 accodato, puoi richiederlo potrebbe essere annullato. Il lavoro può essere annullato dal relativo name, id o da un tag associate.

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 State del lavoro. Se l'opera è finito, non accade nulla. Altrimenti, lo stato del lavoro viene modificato in CANCELLED e il lavoro non verranno eseguite in futuro. Qualsiasi WorkRequest job dipendenti in questo lavoro anche CANCELLED.

Lavoro attualmente in RUNNING riceve una chiamata a ListenableWorker.onStopped(). Sostituisci questo metodo per gestire qualsiasi potenziale pulizia. Vedi interrompere una worker in esecuzione per ulteriori informazioni.

Arresta un worker in esecuzione

Esistono diversi motivi per cui WorkManager potrebbe interrompere l'esecuzione di Worker:

  • Hai richiesto esplicitamente l'annullamento (chiamando WorkManager.cancelWorkById(UUID), ad esempio).
  • Nel caso di un lavoro unico, hai accodato esplicitamente un nuovo WorkRequest con un ExistingWorkPolicy di REPLACE Il WorkRequest precedente viene immediatamente considerato annullato.
  • I vincoli del tuo lavoro non sono più soddisfatti.
  • Il sistema ha chiesto all'app di interrompere il lavoro per qualche motivo. Questo può si verificano se superi la scadenza dell'esecuzione di 10 minuti. L'opera è e pianificato per un nuovo tentativo in un secondo momento.

In queste condizioni, il worker viene arrestato.

Dovresti interrompere in collaborazione il lavoro in corso e rilasciare eventuali e le risorse a cui sta tenendo il tuo worker. Ad esempio, dovresti chiudere a database e file a questo punto. Esistono due meccanismi a livello a disposizione per capire quando il worker si arresta.

Callback onSstop()

Richiami WorkManager ListenableWorker.onStopped() non appena il worker viene arrestato. Sostituisci questo metodo per chiudere le eventuali risorse a cui tieni.

proprietà isSstop()

Puoi chiamare il ListenableWorker.isStopped() per controllare se il worker è già stato interrotto. Se eseguendo operazioni a lunga esecuzione o ripetitive nel Worker, controlla spesso questa proprietà e usala come indicatore per interrompere il lavoro appena possibile il più possibile.

Nota: WorkManager ignora la Result impostata da un worker che ha ricevuto l'indicatore onStop, perché il worker è già considerato è stata interrotta.