• Facile

Ce cours est visible gratuitement en ligne.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Mis à jour le 29/04/2014

Utiliser des tâches Background Agent

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Nous avons vu que Windows Phone ne gérait pas vraiment le multitâches entre les applications… Une application peut être soit démarrée, soit en pause. Si on démarre une nouvelle application, la précédente s’arrête ; les deux ne tournent pas en même temps.
Windows Phone propose un système différent de gestion du multitâches. Ce n’est pas du multitâches à proprement parler, mais la possibilité de faire des choses de manière périodique en tâche de fond, et ce, lorsque l’application est désactivée, en pause. Il s’agit des Background Agent, qui sont donc des tâches qui s’exécutent en arrière-plan. Il existe deux types de tâches de fond, les périodiques (Periodic) qui vont pouvoir faire des petites tâches régulièrement, et celles qui vont avoir besoin de plus de ressources (Resource Intensive) avec fatalement des limitations.

Créer un Background Agent pour une tâche périodique

Une tâche périodique doit être exécutée rapidement et doit faire quelque chose de simple, comme mettre à jour une liste de mails, mettre à jour une tuile, ou mettre à jour une coordonnée GPS. Ces tâches sont exécutées toutes les 30 minutes (plus ou moins précisément, car c’est le système d’exploitation qui gère ces tâches et peut éventuellement en regrouper plusieurs), sont limitées en nombre par téléphone (cela dépend de la configuration du téléphone) et ne dépassent pas 25 secondes d’exécution.
Tandis que les tâches aux ressources intensives peuvent durer jusqu’à 10 minutes, mais ne sont exécutées que lorsque le téléphone est branché à son chargeur, dispose d’une connexion WIFI, à sa batterie rechargée à plus de 90% et que l’écran est verrouillé. C’est typiquement le cas par exemple lorsqu’on recharge le téléphone la nuit…

Un exemple classique d’utilisation de tâche périodique est la mise à jour d’une tuile afin d’informer l’utilisateur qu’il y a quelque chose de nouveau dans l’application à aller consulter, un nouveau mail, des nouveaux flux RSS à lire, etc. Pour les tâches aux ressources intensives, il s’agira plutôt de mettre à jour des gros volumes de données, par exemple télécharger la mise à jour d’une carte pour notre logiciel de navigation GPS ou bien faire des synchronisations lorsque nous avons travaillé en hors-ligne, etc.

Notez que nous n’avons aucune garantie que la tâche soit bien exécutée, c’est le cas par exemple si le téléphone est en mode économie de batterie.

Voyons à présent comment cela fonctionne.

Pour créer une tâche de fond, vous aurez besoin de deux projets. Un premier projet classique contenant votre application classique et un projet spécial contenant la tâche de fond. Pour la démonstration, je vais créer un petit programme dont le but sera d’avoir une tuile qui affiche l’heure de la dernière exécution de la tâche de fond, ainsi qu’un nombre aléatoire.

Créons donc un nouveau projet de type Application Windows Phone que nous nommons DemoAgent, puis ajoutons un nouveau projet à la solution fraîchement créée de type « Agent des tâches planifiées Windows Phone », que nous appelons par exemple AgentMiseAJour (voir figure suivante).

Création du projet de type Agent des tâches planifiées
Création du projet de type Agent des tâches planifiées

C’est dans ce projet que nous allons créer notre tâche de fond qui aura pour but de mettre à jour la tuile. La tâche a d’ailleurs déjà été créée, via la classe ScheduledAgent, qui hérite de ScheduledTaskAgent. Elle possède notamment une méthode OnInvoke qui est le point d’entrée de notre agent. C’est donc dans cette méthode que nous allons mettre à jour la tuile :

protected override void OnInvoke(ScheduledTask task)
{
    ShellTile tuileParDefaut = ShellTile.ActiveTiles.First();

    if (tuileParDefaut != null)
    {
        FlipTileData flipTileData = new FlipTileData
        {
            BackContent = "Dernière MAJ " + DateTime.Now.ToShortTimeString(),
            Count = new Random().Next(0, 20),
        };

        tuileParDefaut.Update(flipTileData);
    }

    NotifyComplete();
}

Vous pouvez constater que nous affichons simplement un message sur le dos de la tuile avec l’heure de dernière mise à jour de la tuile. De même, on détermine un nombre aléatoire qu’on affichera dans le cercle en haut à droite de la tuile.

Mais ceci ne suffit pas. Il va falloir au moins un premier lancement de l’application afin de pouvoir démarrer la tâche et éventuellement permettre de l’arrêter. Dans mon application de démo, je vais donc créer deux boutons permettant de respectivement de démarrer la tâche et de l’arrêter. Voici le XAML :

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <StackPanel>
        <Button Content="Démarrer" Tap="Button_Tap" />
        <Button Content="Arrêter" Tap="Button_Tap_1" />
    </StackPanel>
</Grid>

Maintenant, il faut démarrer et arrêter la tâche. On utilise pour cela la classe ScheduledActionService. Voyons tout d’abord comment arrêter la tâche, car c’est le plus simple. Il suffit de retrouver la tâche par son nom et de la supprimer du service :

public partial class MainPage : PhoneApplicationPage
{
    private const string NomTache = "MajHeure";
    public MainPage()
    {
        InitializeComponent();
    }

    private void Button_Tap(object sender, System.Windows.Input.GestureEventArgs e)
    {

    }

    private void Button_Tap_1(object sender, System.Windows.Input.GestureEventArgs e)
    {
        if (ScheduledActionService.Find(NomTache) != null)
        {
            ScheduledActionService.Remove(NomTache);
        } 
    }
}

La constante nous sert à identifier notre tâche de manière unique. Maintenant, pour démarrer la tâche, nous aurons besoin d’instancier un objet de la classe PeriodicTask, qui comme son nom l’indique, est une tâche périodique :

public partial class MainPage : PhoneApplicationPage
{
    private const string NomTache = "MajHeure";
    public MainPage()
    {
        InitializeComponent();
    }

    private void Button_Tap(object sender, System.Windows.Input.GestureEventArgs e)
    {
        StopTache();

        PeriodicTask task = new PeriodicTask(NomTache);
        task.Description = "Cette description apparaitra dans les paramètres du téléphone";
        try
        {
            ScheduledActionService.Add(task);
            // utilisé à des fins de débogages, pour tenter de démarrer la tâche immédiatement
            ScheduledActionService.LaunchForTest(NomTache, new TimeSpan(0, 0, 1));
        }
        catch (InvalidOperationException)
        {
            MessageBox.Show("Impossible d'ajouter la tâche périodique, car il y a déjà trop d'agents dans le téléphone");
        }
    }

    private void Button_Tap_1(object sender, System.Windows.Input.GestureEventArgs e)
    {
        StopTache();
    }

    public void StopTache()
    {
        if (ScheduledActionService.Find(NomTache) != null)
        {
            ScheduledActionService.Remove(NomTache);
        }
    }
}

Il suffit d’instancier la classe PeriodicTask avec le nom de la tâche, de lui donner une description et de l’ajouter au service. Faites attention à toujours encadrer l’ajout d’une tâche d’un try/catch car une exception peut être levée si nous avons atteint le nombre maximum d’agents que votre téléphone peut supporter.
Ensuite, pour nous faciliter le débogage, il est possible de démarrer la tâche immédiatement, ceci se fait grâce à la méthode LaunchForTest. Cette méthode ne doit pas être appelée directement lorsque l’application est terminée et prête à être publiée.

#if DEBUG
    ScheduledActionService.LaunchForTest(NomTache, new TimeSpan(0, 0, 1));
#endif

Vous aurez remarqué qu’avant de démarrer la tâche, je commence par l’arrêter, si jamais elle est déjà lancée.

Il ne reste plus qu’à démarrer notre application, à cliquer sur le bouton démarrer et à attendre. Si vous mettez un point d’arrêt dans la tâche, vous pourrez voir que vous vous y arrêterez au bout de quelques secondes.
Puis, vous pourrez arrêter l’application, l’épingler dans l’émulateur et attendre à nouveau. La tuile se mettra à jour à peu près toutes les demi-heures, comme le montre la figure suivante.

Mise à jour de la tuile via le background agent
Mise à jour de la tuile via le background agent
Les tâches d'arrière plan dans les paramètres de l'émulateur
Les tâches d'arrière plan dans les paramètres de l'émulateur

Si vous cliquez sur le nom de l’agent, vous pourrez voir la description de la tâche que nous avons saisie. C’est l’endroit idéal pour expliquer à l’utilisateur ce que fait l’agent afin de l’encourager à ne pas le désactiver. Jetez un œil à la figure qui suit.

Description de l'agent
Description de l'agent

Et voilà pour les tâches périodiques.

Vous vous rappelez de notre application météo ? Il s’agit d’une application idéale pour utiliser une tâche en arrière-plan afin de mettre à jour les informations de météo et les indiquer directement dans la tuile, sans avoir à lancer l’application. Ainsi, d’un coup d’œil, nous pourrions voir ce que prévoit la météo pour le temps de la journée.
Je ne vais pas illustrer ceci ici, mais je vous encourage à tester par vous-même cette possibilité dans le cadre d’un petit TP en dehors de ce cours.

Créer une tâche aux ressources intensives

Pour créer une tâche aux ressources intensives, c’est exactement la même chose que pour les tâches périodiques.
La seule différence vient du fait qu’on utilisera la classe ResourceIntensiveTask.
Par extension, on pourra donc déterminer dans notre méthode OnInvoke le type de tâche en testant le type de la classe :

protected override void OnInvoke(ScheduledTask task)
{
    if (task is PeriodicTask)
    {
        // tâche périodique
    }
    else
    {
        // tâche aux ressources intensives
    }

    NotifyComplete();
}

Remarques générales sur les tâches

Gardez à l’esprit qu’une tâche peut ne pas être exécutée du tout si jamais le téléphone ne se retrouve pas dans les bonnes conditions d’exécution de la tâche.
De même, vous aurez intérêt à faire en sorte que vos tâches s’exécutent le plus rapidement possible car elles peuvent être terminées à tout moment si jamais une des conditions d’exécution n’est plus garantie (on débranche le téléphone, on reçoit un coup de fil, etc.).

N’utilisez pas trop de mémoire non plus (moins de 5 Mo) sous peine de voir votre tâche terminée.
Sachez enfin qu’une tâche a une durée de vie de 2 semaines. À chaque fois que votre application est lancée, vous avez l’opportunité de renouveler ces 2 semaines. C’est pour cette raison que nous commençons par supprimer la tâche avant de la rajouter, lorsque nous cliquons sur le bouton démarrer. Ceci implique que si votre application n’est pas utilisée pendant 2 semaines, votre tâche sera automatiquement désactivée.

Envoyer une notification avec un agent d’arrière-plan

Vous vous rappelez des notifications ? Nous avions mis en place toute une architecture complexe pour permettre d’envoyer des notifications sur un téléphone, afin que l’utilisateur puisse être averti de quelque chose sans forcément devoir ouvrir l’application.
Vous vous doutez que l’agent d’arrière-plan est une solution intéressante pour simplifier l’envoi de notifications, en tenant compte bien sûr des limitations de celui-ci. Il suffit de faire en sorte que son agent d’arrière-plan envoie une notification toast locale, après par exemple qu’il soit allé télécharger des informations sur internet. Tout d’abord, créons un agent basique :

public class ScheduledAgent : ScheduledTaskAgent
{
    [… code abrégé pour plus de clarté …]

    protected override void OnInvoke(ScheduledTask task)
    {
        ShellToast toast = new ShellToast
        {
            Title = "Des nouvelles infos !",
            NavigationUri = new Uri("/MainPage.xaml?nouvelleinfo=" + DateTime.Now.ToShortTimeString(), UriKind.Relative),
            Content = "Il est " + DateTime.Now.ToShortTimeString()
        };

        toast.Show();

        NotifyComplete();
    }
}

Vous voyez, on ne fait rien de formidable, juste créer un nouvel objet de type ShellToast, y mettre l’heure et envoyer la notification. Ce serait bien sûr l’emplacement idéal pour aller voir sur internet s’il y a des nouvelles informations à envoyer.
Ensuite, l’enregistrement de l’agent se fait de manière classique dans l’application :

public partial class MainPage : PhoneApplicationPage
{
    private const string NomTache = "EnvoiNotifHeure";

    public MainPage()
    {
        InitializeComponent();

        StopTache();

        PeriodicTask task = new PeriodicTask(NomTache);
        task.Description = "Agent permettant l'envoi de notifications";
        try
        {
            ScheduledActionService.Add(task);
            // utilisé à des fins de débogages, pour tenter de démarrer la tâche immédiatement
#if DEBUG
            ScheduledActionService.LaunchForTest(NomTache, new TimeSpan(0, 0, 1));
#endif
        }
        catch (InvalidOperationException)
        {
            MessageBox.Show("Impossible d'ajouter la tâche périodique, car il y a déjà trop d'agents dans le téléphone");
        }
    }

    public void StopTache()
    {
        if (ScheduledActionService.Find(NomTache) != null)
        {
            ScheduledActionService.Remove(NomTache);
        }
    }
}

Ça peut être plutôt pratique et c’est quand même simple à mettre en place, sans serveur de notification (voir la notification d'arrière-plan dans la figure qui suit).

Notification grâce à l'agent d'arrière plan
Notification grâce à l'agent d'arrière plan
  • Les Background Agent permettent d'exécuter des tâches en arrière-plan, des tâches périodiques ou des tâches aux ressources intensives.

  • Ces tâches peuvent nous permettre facilement de mettre à jour une tuile ou d'envoyer une notification sans que l'utilisateur n'ait besoin de démarrer l'application.

  • Une tâche a une durée de vie de 2 semaines, renouvelable à chaque fois que l'application est démarrée.

Exemple de certificat de réussite
Exemple de certificat de réussite