37 votes

Comment exécuter une méthode en arrière-plan uniquement lorsque l'application est ouverte et en cours d'exécution ?

Une fois que l'application est ouvert et en marche J'aimerais qu'un processus en arrière-plan vérifie une base de données et effectue une mise à jour en fonction des données de la base. Je voudrais que cette vérification ait lieu toutes les minutes. Je veux que cela se produise uniquement lorsque l'application est au premier plan et visible par l'utilisateur.

Quelqu'un peut-il me donner des suggestions sur la manière de procéder ? Je suppose que je peux appeler une méthode à partir d'ici, mais je ne sais pas comment faire. Je ne sais pas non plus comment arrêter ou même si je dois annuler/arrêter manuellement le processus. Le processus s'annule-t-il lorsque l'application n'est pas au premier plan et redémarre-t-il lorsque l'application revient au premier plan ?

public partial class App : Application
{

   protected override void OnStart()
   {
      App.DB.InitData();
      MainPage = new Japanese.MainPage();
   }

Mais dois-je le faire fonctionner sur un fil différent et si oui, comment puis-je le faire ?

Désolé si ma question n'est pas claire. N'hésitez pas à demander et je peux mettre à jour si cela n'a pas de sens.

0 votes

Cela dépend de ce que vous appelez "arrière-plan". Si vous voulez dire le thread non-UI lorsque votre application est en cours d'exécution, alors la réponse de Manish fonctionnera. Si vous avez besoin d'une tâche lorsque votre application est en panne, regardez la réponse de Steven.

0 votes

La seule façon est d'utiliser le TIMER aussi.

23voto

Digitalsa1nt Points 2756

Dans notre application, nous avons utilisé les classes Device.Timer et Stopwatch disponibles dans System.Diagnostics et Xamarin.Forms pour créer une minuterie gérée très générique avec laquelle nous pouvons interagir à l'aide des méthodes onStart, onSleep et onResume de Xamarin.Forms.

Cette solution particulière ne nécessite pas de logique spécifique à la plate-forme, et la minuterie et le chronomètre du dispositif ne bloquent pas l'interface utilisateur.

using Xamarin.Forms;
using System;
using System.Linq;
using System.Diagnostics;

namespace YourNamespace
{
    public partial class App : Application
    {
        private static Stopwatch stopWatch = new Stopwatch();
        private const int defaultTimespan = 1;

        protected override void OnStart()
        {
            // On start runs when your application launches from a closed state, 

            if (!stopWatch.IsRunning)
            {
                stopWatch.Start();
            }

            Device.StartTimer(new TimeSpan(0, 0, 1), () =>
            {
                // Logic for logging out if the device is inactive for a period of time.

                if (stopWatch.IsRunning && stopWatch.Elapsed.Minutes >= defaultTimespan)
                {
                    //prepare to perform your data pull here as we have hit the 1 minute mark   

                        // Perform your long running operations here.

                        Device.InvokeOnMainThread(()=>{
                            // If you need to do anything with your UI, you need to wrap it in this.
                        });

                    stopwatch.Restart();
                }

                // Always return true as to keep our device timer running.
                return true;
            });
        }

        protected override void OnSleep()
        {
            // Ensure our stopwatch is reset so the elapsed time is 0.
            stopWatch.Reset();
        }

        protected override void OnResume()
        {
            // App enters the foreground so start our stopwatch again.
            stopWatch.Start();
        }
    }
}

Edit :

Pour donner un aperçu du fonctionnement de la solution ci-dessus, étape par étape :

L'application démarre à partir d'un état fermé et la méthode 'OnStart()' crée notre Device.Timer qui compte toutes les secondes. Elle démarre également notre chronomètre qui compte jusqu'à une minute.

Lorsque l'application passe en arrière-plan, elle utilise la méthode 'OnSleep'. A ce stade, si nous devions passer une valeur 'false' dans notre action Device.StartTimer(), elle ne redémarrerait pas. Au lieu de cela, nous remettons simplement notre chronomètre à zéro, prêt pour la prochaine ouverture de l'application.

Lorsque l'application revient au premier plan, elle utilise la méthode "OnResume", qui lance simplement le chronomètre existant.

Édition 2018 :

Cette réponse a encore quelques mérites même en 2018, mais principalement pour des situations très spécifiques. Il existe de meilleurs moyens spécifiques à la plateforme pour reproduire cette fonctionnalité, même dans Xamarin.Forms. Ce qui précède reste toujours un moyen agnostique de plateforme pour effectuer une tâche après une période de temps, en tenant compte de l'activité/inactivité de l'utilisateur.

3 votes

Pourquoi utiliser StopWatch ? La minuterie le fait déjà pour vous. StopWatch + Task.Run à l'intérieur du délégué de la minuterie ajoute la réentrance. Et pourquoi utiliser Task.Run pour invoquer le thread principal ? Et pourquoi garder la minuterie toujours en marche ? Et pourquoi StopWatch.restart() ; et Stop() ; au lieu de simplement Reset(); ? maaan

0 votes

@IvanBukashkin Le 'Device.Timer' ne peut pas être utilisé comme un objet passable, c'est une classe statique. Donc, bien que nous puissions le démarrer à un intervalle, nous ne pouvons pas réellement l'arrêter ou le mettre en pause. Le chronomètre nous permet de le faire. J'utilise task run pour les processus longs et 'InvokeOnMainThread' pour effectuer des actions sur l'interface utilisateur. Quant à la réinitialisation et à l'arrêt, le redémarrage convient parfaitement.

0 votes

Utiliser l'indicateur de champ qui arrête le minuteur actuel ou le champ CancellationToken pour arrêter le minuteur. Il n'y a pas du tout besoin d'un chronomètre. Si vous pensez que faire Device.StartTimer à chaque fois est difficile - encapsulez ceci.<br>Vous utilisez Device.StartTimer pour une opération de longue durée. Il n'y a pas besoin de Task.Run.

21voto

Manish Sharma Points 865

Vous pouvez utiliser ceci,

 System.Threading.Tasks.Task.Run(() =>
 {
      //Add your code here.
 }).ConfigureAwait(false);

0 votes

Dois-je donc initier cela à partir de l'App onStart ou du constructeur de l'App ?

0 votes

Vous pouvez l'utiliser en fonction de vos besoins.

0 votes

En utilisant votre code, serais-je en mesure de me connecter immédiatement ? & le code à l'intérieur Task sera-t-il poursuivi en arrière-plan ?

9voto

Ramankingdom Points 1213

Pour exécuter une tâche d'arrière-plan, utilisez un Service . Il classe généralement les tâches en deux catégories : les tâches à long terme et les tâches périodiques.

Le code pour le service dans Android ressemble à ceci

[Service]
public class PeriodicService : Service
{ 
    public override IBinder OnBind(Intent intent)
    {
        return null;
    }

    public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
    {
        // From shared code or in your PCL

        return StartCommandResult.NotSticky;
    }
}

Et pour invoquer le service en arrière-plan

   var intent = new Intent (this, typeof(PeriodicService));
   StartService(intent);

Dans le cas où l'on veut invoquer et vérifier après chaque minute

private void StartBackgroundDataRefreshService ()
{
    var pt = new PeriodicTask.Builder ()
        .SetPeriod (1800) // in seconds; minimum is 30 seconds
        .SetService (Java.Lang.Class.FromType (typeof(BackgroundService)))
        .SetRequiredNetwork (0)
        .SetTag (your package name) // package name
        .Build ();

        GcmNetworkManager.GetInstance (this).Schedule (pt);
}

Pour savoir quel type de service vous convient, lisez ce tutoriel. Types de services

Blog Xamarin pour le service de fond périodique Blog du service Xamarin

L'autre exemple est

public class PeriodicService : Service
{ 
 private static Timer timer = new Timer();     
  public override IBinder OnBind(Intent intent)
    {
        return null;
    }

    public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
    {
        timer.scheduleAtFixedRate(new mainTask(), 0, 5000);
        return StartCommandResult.NotSticky;
    }

   private class mainTask extends TimerTask
    { 
        public void run() 
        {
         //your code
        }
    } 
}

Voici un exemple de code du service Android XAMARIN qui effectuera une tâche toutes les 10 secondes.

using System;
using System.Threading;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Util;

namespace SimpleService
{

[Service]
public class SimpleStartedService : Service
{
    static readonly string TAG = "X:" + typeof(SimpleStartedService).Name;
    static readonly int TimerWait = 10000;
    Timer timer;
    DateTime startTime;
    bool isStarted = false;

    public override void OnCreate()
    {
        base.OnCreate();
    }

    public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
    {
        Log.Debug(TAG, $"OnStartCommand called at {startTime}, flags={flags}, startid={startId}");
        if (isStarted)
        {
            TimeSpan runtime = DateTime.UtcNow.Subtract(startTime);
            Log.Debug(TAG, $"This service was already started, it's been running for {runtime:c}.");
        }
        else
        {
            startTime = DateTime.UtcNow;
            Log.Debug(TAG, $"Starting the service, at {startTime}.");
            timer = new Timer(HandleTimerCallback, startTime, 0, TimerWait);
            isStarted = true;
        }
        return StartCommandResult.NotSticky;
    }

    public override IBinder OnBind(Intent intent)
    {
        // This is a started service, not a bound service, so we just return null.
        return null;
    }

    public override void OnDestroy()
    {
        timer.Dispose();
        timer = null;
        isStarted = false;

        TimeSpan runtime = DateTime.UtcNow.Subtract(startTime);
        Log.Debug(TAG, $"Simple Service destroyed at {DateTime.UtcNow} after running for {runtime:c}.");
        base.OnDestroy();
    }

    void HandleTimerCallback(object state)
    {
        TimeSpan runTime = DateTime.UtcNow.Subtract(startTime);
        Log.Debug(TAG, $"This service has been running for {runTime:c} (since ${state})." );
    }
}

}

0 votes

9voto

Steven Thewissen Points 2408

Il y a plusieurs façons de le faire dans iOS et Android. Dans Xamarin Forms, la plupart de ces fonctionnalités sont regroupées sous le nom de Mise en contexte . Il existe de nombreux tutoriels. Celui-ci est assez élaboré et vaut vraiment la peine d'être consulté :

http://arteksoftware.com/backgrounding-with-xamarin-forms/

Dans Android, une grande partie de ce travail est effectué dans une Service de fond . Pour iOS, consultez Course longue o Tâches de longueur finie . Comme vous pouvez le constater, il n'y a pas de moyen de faire cela avec Xamarin Forms. Vous devrez écrire du code spécifique à Xamarin.Android et Xamarin.iOS.

7voto

Ivan Bukashkin Points 613

Vous pouvez utiliser

Device.StartTimer(TimeSpan.FromMinutes(1), () =>
{
   var shouldTimerContinueWork = true;
   /*your code*/
   return shouldTimerContinueWork;
});

Cette minuterie fonctionne sur un fil d'arrière-plan, utilise l'horloge des dispositifs et est sans danger pour la réentrance.
Pour arrêter cette minuterie quand l'application est en arrière-plan, vous pouvez utiliser Xamarin.Forms.Application méthodes OnSleep y OnResume comme décrit aquí

1 votes

Où mettre ce code, dans la méthode "OnResume" ou dans la méthode "OnStart" ? Parce que vous ne voulez pas créer ce code à chaque fois que le dispositif reprend depuis l'arrière-plan. Si vous le placez dans la méthode 'OnStart', vous n'aurez aucun moyen de le relancer une fois que vous l'aurez arrêté.

0 votes

Sur votre code vous pouvez faire un travail réel, ou juste sauter le tick actuel. cela dépend de plusieurs facteurs. Etes-vous en arrière-plan ou non. Combien de temps s'est écoulé depuis la dernière synchronisation. Est-ce que cela convient à vos besoins ? Je ne sais pas. Donc c'est à vous de voir.

0 votes

Vous pouvez aussi le recréer et effectuer la synchronisation juste au moment de la reprise de l'application, mais faites attention à la réentrance. C'est peut-être mieux ? A vous de voir.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X