web-dev-qa-db-fra.com

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 ouverte et en cours d'exécution, j'aimerais qu'un processus d'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 faire ce chèque toutes les minutes. Je veux seulement que cela se produise lorsque l'application est au premier plan et à la vue de l'utilisateur.

Est-ce que quelqu'un peut me donner quelques 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. Aussi, je ne sais pas comment arrêter ou même si je dois annuler/arrêter manuellement le processus. S'annulerait-il lorsque l'application ne serait pas au premier plan et redémarrerait-il lorsque l'application reviendrait au premier plan?

public partial class App : Application
{

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

Mais dois-je faire en sorte que cela fonctionne sur un autre thread et si oui, comment pourrais-je le faire?.

Désolé si ma question n'est pas claire. S'il vous plaît demander et je peux mettre à jour si cela n'a pas de sens.

32
Alan2

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

Cette solution particulière ne nécessite aucune logique spécifique à la plate-forme, et le chronomètre de périphérique et le chronomètre 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 peu de contexte quant au fonctionnement pas à pas de la solution ci-dessus:

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

Lorsque l'application passe en arrière-plan, elle utilise la méthode 'OnSleep' à ce stade si nous passons une valeur 'false' dans notre action Device.StartTimer (), elle ne redémarrera pas. Donc, au lieu de cela, nous réinitialisons simplement notre chronomètre prêt pour la réouverture de l'application.

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

2018 Edit:

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

17
Digitalsa1nt

vous pouvez utiliser ceci,

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

Pour exécuter une tâche en arrière-plan, utilisez un service . Généralement, les tâches sont classées en tâches longues ou périodiques.

Le code de 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 invoquer le service en arrière-plan

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

Dans le cas où 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 d'arrière-plan périodique Blog 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 de XAMARIN Android Service qui effectuera la 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})." );
    }
}

}

9
Ramankingdom

Il existe plusieurs façons de procéder dans iOS et Android. Dans les formulaires Xamarin, la plupart de ces fonctionnalités sont regroupées sous le pseudonyme de Backgrounding . Il y a beaucoup de tutoriels là-bas. Celui-ci est assez élaboré et vaut vraiment le détour:

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

Dans Android, une grande partie de ce travail est effectuée dans un service en arrière-plan . Pour iOS, recherchez dans Longue exécution ou Tâches de longueur finie . Comme vous pouvez le constater, il n'y a pas de formulaire Xamarin Pour ce faire, vous devrez écrire du code spécifique à Xamarin.Android et Xamarin.iOS.

8
Steven Thewissen

Vous pouvez utiliser

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

Cette minuterie fonctionne sur le fil d’arrière-plan, utilise les périphériques d’horloge et de reentrance safe.
Pour arrêter cette minuterie lorsque l'application est en arrière-plan, vous pouvez utiliser les méthodes Xamarin.Forms.Application OnSleep et OnResume comme décrit ici

7
Ivan Bukashkin

Je fais quelque chose comme ceci est mes applications Xamarin Forms.

public void execute()
        {
            var thread = new Thread(new ThreadStart(startAuthenticationProcess))
            {
                IsBackground = true
            };
            thread.Start();
        }
 private void startAuthenticationProcess()
        {
            Thread.Sleep(2000);
            if (!Utils.isNetworkAvailable(splashActivity))
            {
                splashActivity.RunOnUiThread(() => Utils.showToast(splashActivity, splashActivity.GetString(Resource.String.r30025)));
                splashActivity.FinishAffinity();
            }
            else
            {
                try
                {
                    if (StringUtils.isBlank(strIPAdd) || (StringUtils.isNotBlank(strIPAdd) && (StringUtils.isBlank(strDbName) || "site".Equals(strDbName,StringComparison.OrdinalIgnoreCase))))
                    {
                        splashActivity.RunOnUiThread(() => DependencyService.Get<IAuthenticationDialog>().showAuthenticationDialog(new Command(() =>
                        {
                            var intent = new Intent(splashActivity, typeof(MainActivity));
                            intent.PutExtra("startLoginActivity", false);
                            splashActivity.StartActivity(intent);
                            splashActivity.Finish();
                        })));
                    }
                    else
                    {
                        gotoLoginScreen();
                    }
                }
                catch (Exception e)
                {
                    Log.Error(TAG, e.Message);
                }
            }
        }
5
Abhinash

Facile, essayez quelque chose comme ça et implémentez votre logique dans ces méthodes:

public partial class App : Application
{

   protected override void OnStart()
   {
      // Your App On start code should be here...

      // and then:
      Task.Run(() =>
        {
            //Add your code here, it might looks like:
            CheckDatabase();
            MakeAnUpdateDependingOnDatabase();
        });
   }

J'espère que ça aide.

4
Juan