web-dev-qa-db-fra.com

Planification d'une tâche récurrente dans Android

Je conçois une application qui a pour tâche récurrente d'envoyer de la présence à un serveur dédié tant que l'application est au premier plan.

Lors de mes recherches sur le Web, j'ai découvert différentes approches et je voulais savoir quel était le meilleur moyen de le faire.

Quel est le meilleur moyen de planifier un appel sur le serveur?

Les options que j'ai vues étaient:

  1. Minuterie .

  2. ScheduledThreadPoolExecutor .

  3. Service .

  4. BroadcastReciever avec AlarmManager .

Qu'en penses-tu?

EDIT:
J’ai besoin de cela pour une application basée sur le chat qui envoie toutes les actions de l’utilisateur à un serveur distant.
c'est à dire. l'utilisateur tape un message, l'utilisateur lit un message, l'utilisateur est en ligne, l'utilisateur est hors ligne, etc.

Cela signifie qu'une fois par intervalle, je dois envoyer ce que je fais au serveur. Depuis que j'ouvre une salle de discussion avec d'autres personnes, elles doivent savoir ce que je fais.

Similaire au mécanisme de retour de message WhatsApp: message looks delivered

EDIT # 2:
Les tâches récurrentes devraient maintenant être presque toujours planifiées via l’API JobScheduler (ou FirebaseJobDispatcher pour les API inférieures) afin d’éviter les problèmes de décharge de batterie, comme indiqué dans la section informations vitales). de la Android formation

115
thepoosh

Je ne suis pas sûr mais, selon mes connaissances, je partage mes points de vue. J'accepte toujours la meilleure réponse si je me trompe.

Gestionnaire d'alarmes

Le gestionnaire d'alarmes maintient un verrou de réveil de la CPU tant que la méthode onReceive() du récepteur d'alarme est en cours d'exécution. Cela garantit que le téléphone ne dormira pas tant que vous n'avez pas fini de gérer la diffusion. Une fois que onReceive() revient, Alarm Manager relâche ce verrou de réveil. Cela signifie que, dans certains cas, le téléphone dormira dès que votre méthode onReceive() sera terminée. Si votre récepteur d’alarme a appelé Context.startService(), il est possible que le téléphone s’endorme avant le lancement du service demandé. Pour éviter cela, vos BroadcastReceiver et Service devront implémenter une stratégie de verrouillage de sillage distincte afin de garantir que le téléphone continue de fonctionner jusqu'à ce que le service devienne disponible.

Remarque: Alarm Manager est conçu pour les cas dans lesquels vous souhaitez que votre code d'application soit exécuté à une heure spécifique, même si votre application n'est pas en cours d'exécution. Pour les opérations de chronométrage normales (ticks, timeouts, etc.), il est plus facile et beaucoup plus efficace d’utiliser Handler.

Minuterie

timer = new Timer();

    timer.scheduleAtFixedRate(new TimerTask() {

        synchronized public void run() {

            \\ here your todo;
            }

        }}, TimeUnit.MINUTES.toMillis(1), TimeUnit.MINUTES.toMillis(1));

Timer présente des inconvénients qui sont résolus par ScheduledThreadPoolExecutor. Donc ce n'est pas le meilleur choix

ScheduledThreadPoolExecutor.

Vous pouvez utiliser Java.util.Timer ou ScheduledThreadPoolExecutor (recommandé) pour planifier l'exécution d'une action à intervalles réguliers sur un thread en arrière-plan.

Voici un exemple utilisant ce dernier:

ScheduledExecutorService scheduler =
    Executors.newSingleThreadScheduledExecutor();

scheduler.scheduleAtFixedRate
      (new Runnable() {
         public void run() {
            // call service
         }
      }, 0, 10, TimeUnit.MINUTES);

Alors j'ai préféré ScheduledExecutorService

Pensez également à cela. Si les mises à jour surviennent pendant que votre application est en cours d'exécution, vous pouvez utiliser un Timer, comme suggéré dans d'autres réponses, ou le nouveau ScheduledThreadPoolExecutor. Si votre application est mise à jour même lorsqu'elle n'est pas en cours d'exécution, vous devez utiliser l'option AlarmManager.

Alarm Manager est conçu pour les cas dans lesquels vous voulez que votre code d'application soit exécuté à une heure spécifique, même si votre application n'est pas en cours d'exécution.

Notez que si vous prévoyez de mettre à jour votre application lorsque celle-ci est désactivée, une fois toutes les dix minutes est assez fréquente, et donc peut-être un peu trop énergivore.

160
Md Maidul Islam

Minuteur

Comme mentionné sur le javadocs , il vaut mieux utiliser un ScheduledThreadPoolExecutor.

ScheduledThreadPoolExecutor

Utilisez cette classe lorsque votre cas d'utilisation nécessite plusieurs threads de travail et que l'intervalle de veille est faible. Petit comment? Eh bien, je dirais environ 15 minutes. La AlarmManager commence les intervalles de planification à ce moment et il semble suggérer que pour des intervalles de sommeil plus courts, cette classe peut être utilisée. Je n'ai pas de données pour appuyer la dernière déclaration. C'est une intuition.

Un service

Votre service peut être fermé à tout moment par la VM. N'utilisez pas de services pour des tâches récurrentes. Une tâche récurrente peut démarrer un service, ce qui est tout autre chose.

BroadcastReciever avec AlarmManager

Pour des intervalles de sommeil plus longs (> 15 minutes), c'est la voie à suivre. AlarmManager a déjà des constantes (AlarmManager.INTERVAL_DAY) suggérant qu'il peut déclencher des tâches plusieurs jours après sa planification initiale. Il peut également réveiller le processeur pour exécuter votre code.

Vous devez utiliser l'une de ces solutions en fonction de votre calendrier et de vos besoins en threads de travail.

30
Deepak Bala

Je me rends compte que c’est une vieille question à laquelle on a répondu, mais cela pourrait aider quelqu'un. Dans votre activity

private ScheduledExecutorService scheduleTaskExecutor;

Dans onCreate

  scheduleTaskExecutor = Executors.newScheduledThreadPool(5);

    //Schedule a task to run every 5 seconds (or however long you want)
    scheduleTaskExecutor.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            // Do stuff here!

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    // Do stuff to update UI here!
                    Toast.makeText(MainActivity.this, "Its been 5 seconds", Toast.LENGTH_SHORT).show();
                }
            });

        }
    }, 0, 5, TimeUnit.SECONDS); // or .MINUTES, .HOURS etc.
12
Emzor

Citer le Planification des alarmes répétitives - Comprendre les compromis docs:

Un scénario courant pour déclencher une opération en dehors de la durée de vie de votre application consiste à synchroniser des données avec un serveur. C'est un cas où vous pourriez être tenté d'utiliser une alarme à répétition. Toutefois, si vous possédez le serveur hébergeant les données de votre application, l'utilisation de Google Cloud Messaging (GCM) conjointement avec l'adaptateur de synchronisation constitue une meilleure solution que AlarmManager. Un adaptateur de synchronisation vous offre les mêmes options de planification que AlarmManager, mais il vous offre beaucoup plus de flexibilité.

Par conséquent, le meilleur moyen de planifier un appel sur le serveur consiste à utiliser Google Cloud Messaging (GCM) en association avec adaptateur de synchronisation .

2
tato.rodrigo

J'ai créé une tâche ponctuelle dans laquelle la tâche que l'utilisateur souhaite répéter doit ajouter la méthode run () personnalisée TimeTask. il se reproduit avec succès.

 import Java.text.SimpleDateFormat;
 import Java.util.Calendar;
 import Java.util.Timer;
 import Java.util.TimerTask;

 import Android.os.Bundle;
 import Android.view.View;
 import Android.view.View.OnClickListener;
 import Android.widget.Button;
 import Android.widget.CheckBox;
 import Android.widget.TextView;
 import Android.app.Activity;
 import Android.content.Intent;

 public class MainActivity extends Activity {

     CheckBox optSingleShot;
     Button btnStart, btnCancel;
     TextView textCounter;

     Timer timer;
     MyTimerTask myTimerTask;

     int tobeShown = 0  ;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    optSingleShot = (CheckBox)findViewById(R.id.singleshot);
    btnStart = (Button)findViewById(R.id.start);
    btnCancel = (Button)findViewById(R.id.cancel);
    textCounter = (TextView)findViewById(R.id.counter);
    tobeShown = 1;

    if(timer != null){
        timer.cancel();
    }

    //re-schedule timer here
    //otherwise, IllegalStateException of
    //"TimerTask is scheduled already" 
    //will be thrown
    timer = new Timer();
    myTimerTask = new MyTimerTask();

    if(optSingleShot.isChecked()){
        //singleshot delay 1000 ms
        timer.schedule(myTimerTask, 1000);
    }else{
        //delay 1000ms, repeat in 5000ms
        timer.schedule(myTimerTask, 1000, 1000);
    }

    btnStart.setOnClickListener(new OnClickListener(){

        @Override
        public void onClick(View arg0) {


            Intent i = new Intent(MainActivity.this, ActivityB.class);
            startActivity(i);

            /*if(timer != null){
                timer.cancel();
            }

            //re-schedule timer here
            //otherwise, IllegalStateException of
            //"TimerTask is scheduled already" 
            //will be thrown
            timer = new Timer();
            myTimerTask = new MyTimerTask();

            if(optSingleShot.isChecked()){
                //singleshot delay 1000 ms
                timer.schedule(myTimerTask, 1000);
            }else{
                //delay 1000ms, repeat in 5000ms
                timer.schedule(myTimerTask, 1000, 1000);
            }*/
        }});

    btnCancel.setOnClickListener(new OnClickListener(){

        @Override
        public void onClick(View v) {
            if (timer!=null){
                timer.cancel();
                timer = null;
            }
        }
    });

}

@Override
protected void onResume() {
    super.onResume();

    if(timer != null){
        timer.cancel();
    }

    //re-schedule timer here
    //otherwise, IllegalStateException of
    //"TimerTask is scheduled already" 
    //will be thrown
    timer = new Timer();
    myTimerTask = new MyTimerTask();

    if(optSingleShot.isChecked()){
        //singleshot delay 1000 ms
        timer.schedule(myTimerTask, 1000);
    }else{
        //delay 1000ms, repeat in 5000ms
        timer.schedule(myTimerTask, 1000, 1000);
    }
}


@Override
protected void onPause() {
    super.onPause();

    if (timer!=null){
        timer.cancel();
        timer = null;
    }

}

@Override
protected void onStop() {
    super.onStop();

    if (timer!=null){
        timer.cancel();
        timer = null;
    }

}

class MyTimerTask extends TimerTask {

    @Override
    public void run() {

        Calendar calendar = Calendar.getInstance();
        SimpleDateFormat simpleDateFormat = 
                new SimpleDateFormat("dd:MMMM:yyyy HH:mm:ss a");
        final String strDate = simpleDateFormat.format(calendar.getTime());

        runOnUiThread(new Runnable(){

            @Override
            public void run() {
                textCounter.setText(strDate);
            }});
    }
}

}

1
hitesh141