Gestion des travaux

Une fois que vous avez défini votre Worker et votre WorkRequest, la dernière étape consiste à placer vos travaux en file d'attente. Le moyen le plus simple de placer un travail en file d'attente est d'appeler la méthode WorkManager enqueue() en transmettant la valeur WorkRequest que vous souhaitez exécuter.

Kotlin


val myWork: WorkRequest = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork)

Java


WorkRequest myWork = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork);

Faites attention à ne pas créer de doublons lorsque vous mettez des travaux en file d'attente. Par exemple, une application peut essayer d'importer ses journaux dans un service de backend toutes les 24 heures. Si vous ne faites pas attention, vous risquez de mettre la même tâche en file d'attente à plusieurs reprises, même s'il n'est nécessaire de l'exécuter qu'une seule fois. Pour atteindre cet objectif, vous pouvez programmer cette tâche comme un travail unique.

Travail unique

Le travail unique est un concept puissant qui garantit qu'une seule instance de travail dispose d'un nom spécifique à la fois. Contrairement aux identifiants, les noms uniques sont lisibles et spécifiés par le développeur au lieu d'être générés automatiquement par WorkManager. Contrairement aux balises, les noms uniques ne sont associés qu'à une seule instance de travail.

Un travail unique peut être appliqué à un travail ponctuel ou périodique. Vous pouvez créer une séquence de travail unique en appelant l'une de ces méthodes, selon que vous planifiez un travail ponctuel ou périodique.

Ces deux méthodes acceptent trois arguments :

  • uniqueWorkName – String permettant d'identifier de manière unique la demande de travail.
  • existingWorkPolicy – enum indiquant à WorkManager ce qu'il faut faire en cas de chaîne de travail inachevée avec ce nom unique. Pour en savoir plus, consultez le Règlement concernant la résolution des conflits.
  • work – WorkRequest à planifier.

Grâce au travail unique, nous pouvons résoudre le problème de planification en double mentionné plus haut.

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);

Si le code s'exécute alors qu'une tâche sendLogs figure déjà dans la file d'attente, la tâche existante est conservée et aucune nouvelle tâche n'est ajoutée.

Les séquences de travail uniques peuvent également être utiles si vous avez besoin de créer une longue chaîne de tâches progressivement. Par exemple, une application de retouche photo peut permettre aux utilisateurs d'annuler une longue chaîne d'actions. Les opérations d'annulation individuelles peuvent prendre un certain temps, mais elles doivent être effectuées dans le bon ordre. Dans ce cas, l'application peut créer une chaîne "annuler" et ajouter chaque opération d'annulation à la chaîne si nécessaire. Pour en savoir plus, consultez Enchaînement de travaux.

Règlement concernant la résolution des conflits

Lorsque vous planifiez un travail unique, vous devez indiquer à WorkManager l'action à effectuer en cas de conflit. Pour ce faire, transmettez une énumération lorsque vous mettez la tâche en file d'attente.

Pour un travail ponctuel, vous devez fournir un objet ExistingWorkPolicy, qui accepte quatre options de gestion du conflit.

  • REPLACE (remplacer) une tâche existante par une nouvelle tâche. Cette option annule la tâche existante.
  • KEEP (conserver) une tâche existante et ignorer les nouvelles tâches.
  • APPEND (ajouter) les nouvelles tâches à la fin des tâches existantes. Avec ce règlement, vos nouvelles tâches seront associées aux tâches existantes, une fois celles-ci terminées.

Les tâches existantes deviennent un prérequis pour les nouvelles tâches. Si les tâches existantes sont CANCELLED ou FAILED, les nouvelles tâches seront également CANCELLED ou FAILED. Si vous souhaitez exécuter les nouvelles tâches, quel que soit l'état des tâches existantes, utilisez plutôt APPEND_OR_REPLACE.

  • APPEND_OR_REPLACE fonctionne de la même manière que APPEND, sauf qu'il ne dépend pas de l'état des tâches prérequis. Si les tâches existantes sont CANCELLED ou FAILED, les nouvelles tâches s'exécutent toujours.

Pour les tâches régulières, vous devez spécifier un objet ExistingPeriodicWorkPolicy qui accepte deux options : REPLACE et KEEP. Ces options fonctionnent de la même manière que les équivalents ExistingWorkPolicy.

Observer votre travail

À tout moment après avoir mis une tâche en file d'attente, vous pouvez vérifier son état en interrogeant WorkManager par son name, son id ou une tag associée.

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 requête renvoie un ListenableFuture d'un objet WorkInfo, qui inclut l'id de la tâche, ses balises, son State actuel et des données de sortie définies via Result.success(outputData).

Une variante LiveData de chacune des méthodes vous permet d'observer les modifications apportées à WorkInfo en enregistrant un écouteur. Par exemple, si vous souhaitez afficher un message lorsque l'utilisateur termine une tâche, vous pouvez le configurer comme suit :

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();
   }
});

Requêtes de travail complexes

WorkManager 2.4.0 et versions ultérieures acceptent les requêtes complexes pour les tâches mises en file d'attente à l'aide d'objets WorkQuery. WorkQuery permet d'effectuer des requêtes concernant des travaux en associant une ou plusieurs balises, l'état et le nom de travail unique.

L'exemple suivant montre comment trouver tous les travaux avec la balise "syncTag", qui sont dans l'état FAILED ou CANCELLED et dont le nom de travail unique est "preProcess" ou "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);

Chaque composant (balise, état ou nom) d'une WorkQuery est mis en rapport via AND avec les autres. Chaque valeur d'un composant est mise en rapport via OR. Exemple : (name1 OR name2 OR ...) AND (tag1 OR tag2 OR ...) AND (state1 OR state2 OR ...).

WorkQuery fonctionne également avec l'équivalent LiveData getWorkInfosLiveData().

Annuler et arrêter un travail

Si vous n'avez plus besoin d'exécuter un travail précédemment placé en file d'attente, vous pouvez demander son annulation. Une tâche peut être annulée via son name, son id ou une tag associée.

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");

En arrière-plan, WorkManager vérifie le State de la tâche. Si l'opération est déjà terminée, rien ne se passe. Sinon, l'état de la tâche est définie sur CANCELLED, et la tâche ne s'exécute plus par la suite. Toutes les tâches WorkRequest dépendantes de ce travail seront également CANCELLED.

Actuellement, le travail RUNNING reçoit un appel à ListenableWorker.onStopped(). Ignorez cette méthode pour gérer tout nettoyage potentiel. Pour en savoir plus, consultez Arrêter un nœud de calcul en cours d'exécution.

Arrêter un nœud de calcul en cours d'exécution

Votre Worker peut être arrêté par WorkManager pour différentes raisons :

  • Vous avez explicitement demandé son annulation (en appelant WorkManager.cancelWorkById(UUID), par exemple).
  • Dans le cas d'un travail unique, vous avez explicitement mis en file d'attente un nouvel élément WorkRequest avec un élément ExistingWorkPolicy défini sur REPLACE. L'ancienne WorkRequest est immédiatement considérée comme étant annulée.
  • Les contraintes de votre travail ne sont plus respectées.
  • Le système a demandé à votre application d'arrêter votre travail pour une raison inconnue. Cela peut se produire si vous dépassez le délai d'exécution de 10 minutes. Le travail est programmé pour être relancé ultérieurement.

Dans ces conditions, votre nœud de calcul est arrêté.

Vous devez coopérer pour abandonner tout travail en cours et libérer toutes les ressources que votre nœud de calcul détient. Par exemple, vous devez fermer les handles ouvertes des bases de données et des fichiers à ce stade. Deux mécanismes sont à votre disposition pour comprendre quand votre nœud de calcul s'arrête.

Rappel onStopped()

WorkManager appelle ListenableWorker.onStopped() dès que votre nœud de calcul a été arrêté. Ignorez cette méthode pour fermer les ressources que vous pourriez conserver.

Propriété isStopped()

Vous pouvez appeler la méthode ListenableWorker.isStopped() pour vérifier si votre nœud de calcul a déjà été arrêté. Si vous effectuez des opérations de longue durée ou répétitives dans votre nœud de calcul, vous devez souvent vérifier cette propriété et l'utiliser comme signal pour arrêter des tâches dès que possible.

Remarque : WorkManager ignore le Result défini par un nœud de calcul ayant reçu le signal onStop, car il est déjà considéré comme étant arrêté.