Il existe plusieurs façon de planifier une tâche sous Android comme sous iOS ou Windows, mais ce qui nous intéresse ici c’est de le faire le plus simplement possible sous Android, comment ?
Seulement Android
Pourquoi ce choix restrictif ? Tout simplement en raison des différences majeures entre chaque OS faisant que la ou les astuces à utiliser sont tellement éloignées qu’il est préférable de traiter chaque OS à part. Aujourd’hui j’ai décidé de parler de celui qui est le plus utilisé sur mobiles, Android.
J’aborderai certainement un jour la problématique sous iOS ou Windows mais à chaque jour suffit sa peine ne dit-on pas…
Un Nuget pour simplifier
En effet pour aller au plus simple du plus simple je vais vous montrer comment utiliser un Nuget très pratique “WorkManager”. Android c’est le souk il faut bien l’admettre, un OS qui n’a pas la tenue et la cohérence de ce que fut même Windows Phone. Il y des tonnes de versions, de breaking changes, des noms rigolos mais qui sont assortis de numéros de version qui, on ne sait pourquoi, ne sont pas alignés avec les numéros de version du SDK correspondant, etc. Franchement c’est dommage que cet OS soit celui que les “gens” aient préféré, c’était le pire de ceux qui étaient en lice…
Bref il faut gérer ce qui est avant l’API 14, ou après, et plein d’autres choses du genre car ce ne sont même pas les mêmes objets qu’il faut utiliser.
Un bon gars s’est chargé de faire le clean dans tout ça, remercions-le (c’est Jon Douglas) et utilisons son Nuget “WorkManager” ! Attention toutefois cela ne marche qu’à partir de l’API version 14 et les versions suivantes en prenant en compte JobScheduler, AlarmManager et BroadcastReceiver. Je vous ai dit que c’est le souk…
D’ailleurs il existe d’autres voies pour atteindre l’objectif, parmi elles :
- Google Cloud Messaging
- Firebase Cloud Messaging
- DownloadManager
- Foreground Service
- Alarm Manager
- etc…
WorkManager
C’est une librairie qui rend les choses plus faciles donc. Elle permet de planifier une tâche asynchrone même si l’App n’est pas en fonction ou que la device vient de démarrer. Le minimum étant l’API de niveau 14. En dessous il faudra vous débrouiller mais on parle de smartphone préhistoriques…
A partir de l’API 23 et au-delà WorkManager utilisera JobScheduler. En dessous il fera son boulot en utilisant AlarmManager plus BrodcastReceivers.
Vous devrez aussi cibler l’API 28 (ou au-dessus) dans votre application sinon ça ne marchera pas (ce qui n’interdit pas d’avoir une version minimum descendant donc jusqu’à la 14 au plus bas).
Comment la tâche est-elle exécutée ?
Le travail a effectué est confié à un “exécuteur” pour garantir que le job est fait :
L’exécuteur va accomplir la tâche tant qu’elle répond aux critères que vous aurez définis lors de la mise en file d’attente du job.
Comme je le mentionnais plus haut la tâche à exécuter sera Async par défaut. Et tout WorkManager fonctionne en asynchrone vous n’aurez ainsi pas à vous poser de question sur les threads à utiliser ou pas. Les jobs sont stockés dans une base de données qui sert à valider tous les jobs empilés, ceux qui ont marché comme ceux qui ont échoué.
Quand une tâche se termine-t-elle ?
La fin d’une tâche peut arriver dans les conditions suivantes :
- À la fin des travaux.
- Dans le cas où les contraintes ne seraient plus respectées (le réseau pourrait être perdu, le téléphone n'est plus connecté, etc.).
- L'OS a décidé de tuer votre travail en file d'attente.
- Vous avez annulé votre travail.
Durée d’une tâche
Tâche unique
L’état final de ce type de tâche est “réussi” une fois qu’elle est terminée. Sinon elle passera à “échoué” ou “annulé”.
Tâche périodique
WorkManager est capable de gérer deux types de tâches, celles qui doivent être exécutées une fois (ci-dessus) et celles qu’on planifie à intervalle régulier par exemple. Le cycle de vie devient alors :
Ce type particulier de travail n’a pas vraiment d’état final puisqu’il a un nombre fini ou infini de répétitions. Il peut réussir à chaque fois, échouer, ou être annulé, cela ne concernera qu’une des itérations et il sera relancer autant de fois que nécessaire selon sa programmation.
Le package
Avant d’utiliser le WorkManager il faut installer le paquet dans votre application, et cela uniquement dans la partie Android native. Les Xamarin.Forms ne jouent pas forcément ici, on est plutôt dans la partie Xamarin.Android. Bien entendu la tâche à exécuter pourra utiliser des classes définies dans la partie commune sauf ce qui est visuel ou qui dépend de l'App qui n’est pas garantie d’être lancée.
Le package a installé s’appelle Xamarin.Android.Arch.Work.Runtime (https://www.nuget.org/packages/Xamarin.Android.Arch.Work.Runtime/)
Création d’une tâche d’arrière-plan
Une chose à bien comprendre est que la tâche va être exécutée quand le moment sera venu, appli lancée ou non. Si elle est lancée il ne faut pas que la tâche vienne interférer avec ce que fait l’utilisateur… Il y a donc un minimum de précautions à prendre surtout si vous utilisez des classes ou services définis dans la partie Xamarin.Forms commune. On parle ici de fonctions réservées aux développeurs un peu confirmés qui savent donc ce qu’il faut faire ou ne pas faire…
On définit une tâche en utilisant la classe Worker. La méthode est exécutée sur un thread d’arrière-plan fourni par WorkManager.DoWork().
La méthode retenue consiste à étendre la classe Worker et de faire un override de la méthode DoWork() :
public class CalculatorWorker : Worker
{
public CalculatorWorker(Context context, WorkerParameters workerParameters)
:base(context, workerParameters)
{
}
public override Result DoWork()
{
var taxReturn = CalculateTaxes();
Android.Util.Log.Debug("CalculatorWorker", $"Your Tax Return is: {taxReturn}");
return Result.InvokeSuccess();
}
public double CalculateTaxes()
{
return 2000;
}
}
Le “Result” retourné par DoWork() informe de l’état de la tâche à sa sortie : Result.Success, Result.Failure et Result.Retry.
Le module de travail ne connait que les paramètres qui lui ont été fournis via les WorkerParameters. Essayez de vous limiter à ces informations et limitez autant que vous le pouvez les interactions avec le code commun. J’ai dit que cela était possible, mais c’est touchy… Donc si vous ne savez pas trop pourquoi c’est délicat, ne le faites pas !
Quand et comment exécuter une tâche ?
La classe Worker que nous venons de voir s’intéresse juste au travail à exécuter. Pour planifier la tâche il faut utiliser WorkRequest. Les tâches peuvent être ponctuelles ou répétitives. Pour cela on utilisera OnTimeWorkRequest ou bien PeriodicTimeWorkRequest dont les noms sont éloquents.
Pour le Worker exemple ci-avant voici le WorkRequest à construire :
PeriodicWorkRequest taxWorkRequest =
PeriodicWorkRequest.Builder.From<CalculatorWorker>(TimeSpan.FromMinutes(20)).Build();
Ici la tâche sera périodique et répétée toutes les 20 minutes.
Lancer la tâche
Ce travail sera bien entendu automatique, mais j’entends par “lancer” ici le fait de donner l’ordre à un moment donné de mettre la tâche dans la file d’attente, donc de la planifier.
Une fois la classe Worker spécialisée, une fois la WorkRequest programmée ; il ne reste donc plus qu’à utiliser directement WorkManager :
WorkManager.Instance.Enqueue(taxWorkRequest);
Surveiller ce qu’il se passe
Le débogue de ce genre de code est particulièrement délicat.
S’agissant de code natif exécuté par l’OS le débogueur de Visual Studio ne sera pas d’une grande aide… On peut en revanche utiliser ADB logcat pour voir la tâche programmée et ses divers états d’exécution :
2020-11-10 14:11:51.240 6364-6402/com.companyname D/CalculatorWorker: Your Tax Return is: 2000
2020-11-10 14:11:51.242 6364-6390/com.companyname I/WM-WorkerWrapper: Worker result SUCCESS for Work
[ id=3735e0f9-fc12-41aa-9f51-ac856b6eac18, tags={ md5592692d8f1b606f6fbb81503ff802e0e.CalculatorWorker } ]
2020-11-10 14:21:33.020 6364-6404/com.companyname D/CalculatorWorker: Your Tax Return is: 2000
2020-11-10 14:21:33.023 6364-6390/com.companyname I/WM-WorkerWrapper: Worker result SUCCESS for Work
[ id=54daa9ec-d81f-4de1-b138-d5ff6349b088, tags={ md5592692d8f1b606f6fbb81503ff802e0e.CalculatorWorker } ]
Conclusion
Ce package permet d’accéder à une fonction souvent nécessaire mais de façon assez simple. Sa documentation est succincte et il faudra plonger dans le code source pour en découvrir toutes les astuces (comme l’annulation d’une tâche par exemple). Mais le cycle de vie de la tâche est pris en fonction par le WorkManager ce qui soulage beaucoup.
Il ne reste pas moins vrai que ce type de programmation native proche de l’OS et de ses fonctions réclame un peu de doigté et quelques connaissances pour ne pas faire des bêtises. Malgré l’aide apportée ce genre de code reste difficile à déboguer et peut facilement mettre le bazar dans la machine. Allez-y par étapes et assurez-vous que votre tâche fonctionne déjà bien en dehors de toute planification (vous mettez un bouton, et vous l’exécutez et la suivez au débogueur de VS par exemple).
Je ne veux pas vous faire peur non plus mais quelques petits avertissements valent mieux que de gros bobos !
Stay Tuned !