Planen wiederkehrender Aufgaben in Android


122

Ich entwerfe eine App, die eine wiederkehrende Aufgabe hat, Präsenz an einen dedizierten Server zu senden, solange die App im Vordergrund steht.

Bei meinen Recherchen im Internet habe ich einige unterschiedliche Ansätze gesehen und wollte wissen, wie dies am besten funktioniert.

Wie kann ein Serveranruf am besten geplant werden?

Die Optionen, die ich sah, waren:

  1. Timer .

  2. ScheduledThreadPoolExecutor .

  3. Service .

  4. BroadcastReciever mit AlarmManager .

Was ist deine Meinung?

BEARBEITEN:
Der Grund, warum ich dies benötige, ist eine Chat-basierte App, die alle Benutzeraktionen an einen Remote-Server sendet.
dh der Benutzer gibt eine Nachricht ein, der Benutzer liest eine Nachricht, der Benutzer ist online, der Benutzer ist offline usw.

Dies bedeutet, dass ich einmal in jedem Intervall dem Server senden muss, was ich tue, da ich einen Chatraum mit anderen Personen öffne, müssen diese wissen, was ich tue.

Ähnlich wie beim WhatsApp-Nachrichten-Feedback-Mechanismus: Nachricht sieht zugestellt

BEARBEITEN 2:
Wiederkehrende Aufgaben sollten jetzt fast immer über die JobSchedulerAPI (oder FirebaseJobDispatcherfür niedrigere APIs) geplant werden, um Probleme mit dem Entladen des Akkus zu vermeiden, wie im Abschnitt "Wichtige Informationen" des Android-Trainings zu lesen ist

EDIT # 3:
FirebaseJobDispatcher wurde veraltet und durch Workmanager ersetzt , der auch Funktionen von JobScheduler enthält.


2
BroaccastReceiver mit AlarmManager ist ziemlich einfach zu bedienen. Es ist die einzige der oben genannten Alternativen, die ich ausprobiert habe.

1
Es gibt wenig Grund, einen Timer über einen ScheduledThreadPoolExecutor zu verwenden, der flexibler ist, da er mehr als einen Hintergrundthread zulässt, eine bessere Auflösung aufweist (nur für die Auflösung von ms nützlich) und die Ausnahmebehandlung ermöglicht. Wie für den Alarmmanager, dieser Beitrag gibt einige Informationen über den Unterschied.
Assylias

Für einen kurzen Lebenszyklus, dh alle 30 Sekunden eine Aufgabe in einer Aktivität ausführen, die sich derzeit im Vordergrund befindet, ist die Verwendung von ScheduledThreadPoolExecutor (oder Timer) effizienter. Für einen langen Lebenszyklus, dh alle 1 Stunde eine Aufgabe in einem Hintergrunddienst ausführen, bietet die Verwendung von AlarmManager mehr Zuverlässigkeit.
Yorkw

Warum müssen Sie den Versand überhaupt planen? Warum senden Sie Ihre App-Beschreibung nicht einfach in Echtzeit?
iTech

weil der Benutzer davon ausgeht, dass Sie online sind, und eine Zeitüberschreitung verwendet. Das heißt, wenn ich in der letzten X-Zeit keine "Anwesenheit" - oder "Tipp" -Nachricht erhalten habe,
gehe

Antworten:


164

Ich bin mir nicht sicher, aber nach meinem Wissen teile ich meine Ansichten. Ich akzeptiere immer die beste Antwort, wenn ich falsch liege.

Alarm Manager

Der Alarm Manager hält eine CPU-Wecksperre, solange die onReceive()Methode des Alarmempfängers ausgeführt wird. Dies garantiert, dass das Telefon erst dann in den Ruhezustand wechselt, wenn Sie die Übertragung abgeschlossen haben. Nach der onReceive()Rückkehr gibt der Alarm Manager diese Wecksperre frei. Dies bedeutet, dass das Telefon in einigen Fällen in den Ruhezustand wechselt, sobald Ihre onReceive()Methode abgeschlossen ist. Wenn Ihr Alarmempfänger anruft Context.startService(), wird das Telefon möglicherweise in den Ruhezustand versetzt, bevor der angeforderte Dienst gestartet wird. Um dies zu verhindern, Ihre BroadcastReceiverund Servicebenötigen eine separate Wake Lock Politik umzusetzen , um sicherzustellen , dass das Telefon läuft weiter , bis der Dienst verfügbar wird.

Hinweis: Der Alarm Manager ist für Fälle vorgesehen, in denen Ihr Anwendungscode zu einem bestimmten Zeitpunkt ausgeführt werden soll, auch wenn Ihre Anwendung derzeit nicht ausgeführt wird. Für normale Timing-Vorgänge (Ticks, Timeouts usw.) ist die Verwendung von Handler einfacher und effizienter.

Timer

timer = new Timer();

    timer.scheduleAtFixedRate(new TimerTask() {

        synchronized public void run() {

            \\ here your todo;
            }

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

Timerhat einige Nachteile, die durch gelöst werden ScheduledThreadPoolExecutor. Es ist also nicht die beste Wahl

ScheduledThreadPoolExecutor .

Sie können java.util.Timeroder ScheduledThreadPoolExecutor(bevorzugt) verwenden, um eine Aktion zu planen, die in regelmäßigen Abständen in einem Hintergrundthread ausgeführt wird.

Hier ist ein Beispiel mit letzterem:

ScheduledExecutorService scheduler =
    Executors.newSingleThreadScheduledExecutor();

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

Also habe ich es vorgezogen ScheduledExecutorService

Denken Sie aber auch daran, dass Sie, wenn die Aktualisierungen während der Ausführung Ihrer Anwendung erfolgen, a Timer, wie in anderen Antworten vorgeschlagen, oder die neuere verwenden können ScheduledThreadPoolExecutor. Wenn Ihre Anwendung auch dann aktualisiert wird, wenn sie nicht ausgeführt wird, sollten Sie sich für das entscheiden AlarmManager.

Der Alarm Manager ist für Fälle vorgesehen, in denen Ihr Anwendungscode zu einem bestimmten Zeitpunkt ausgeführt werden soll, auch wenn Ihre Anwendung derzeit nicht ausgeführt wird.

Beachten Sie, dass, wenn Sie beim Ausschalten Ihrer Anwendung eine Aktualisierung planen, diese alle zehn Minuten häufig auftritt und daher möglicherweise etwas zu viel Strom verbraucht.


Ich probiere diese Methode für eine periodische Aufgabe aus, aber es scheint nicht zu funktionieren stackoverflow.com/questions/27872016/…
dowjones123

Für einfache Dinge - wie das Überprüfen des Status alle n Sekunden - reicht Timer aus.
IgorGanapolsky

1
@ Maid786 Was sollten wir verwenden, wenn wir eine Aufgabe (wie das Senden von Benachrichtigungen) im Abstand von einer Woche oder einer Dauer in Tagen ausführen möchten? Wird Alarm Manager dafür zu viel Hintergrundberechnung oder -verarbeitung in Anspruch nehmen?
Chintan Shah

30

Timer

Wie in den Javadocs erwähnt , ist es besser, einen ScheduledThreadPoolExecutor zu verwenden.

ScheduledThreadPoolExecutor

Verwenden Sie diese Klasse, wenn für Ihren Anwendungsfall mehrere Arbeitsthreads erforderlich sind und das Ruheintervall klein ist. Wie klein ? Nun, ich würde ungefähr 15 Minuten sagen. Die AlarmManagerStartzeitintervalle zu diesem Zeitpunkt und es scheint darauf hinzudeuten, dass diese Klasse für kleinere Schlafintervalle verwendet werden kann. Ich habe keine Daten, um die letzte Aussage zu stützen. Es ist eine Ahnung.

Bedienung

Ihr Dienst kann jederzeit von der VM geschlossen werden. Verwenden Sie Dienste nicht für wiederkehrende Aufgaben. Eine wiederkehrende Aufgabe kann einen Dienst starten , was eine ganz andere Sache ist.

BroadcastReciever mit AlarmManager

Für längere Schlafintervalle (> 15 Minuten) ist dies der richtige Weg. AlarmManagerKonstanten ( AlarmManager.INTERVAL_DAY) deuten bereits darauf hin, dass Aufgaben mehrere Tage nach ihrer ursprünglichen Planung ausgelöst werden können. Es kann auch die CPU aufwecken, um Ihren Code auszuführen.

Sie sollten eine dieser Lösungen verwenden, die auf Ihrem Timing und den Anforderungen des Worker-Threads basiert.


1
Was wäre, wenn ich die App verwenden und jede halbe Stunde ein Backup erstellen möchte? Aber ich möchte kein Backup erstellen, während die App nicht verwendet wird (das wäre eine totale Verschwendung). Alarmmanager wiederholt die Aktion kontinuierlich bis zum Neustart (das habe ich zumindest gehört). Was würden Sie empfehlen? ScheduledThreadPoolExecutor oder Alarmmanager?
Hasdrubal

13

Mir ist klar, dass dies eine alte Frage ist und beantwortet wurde, aber dies könnte jemandem helfen. In deinemactivity

private ScheduledExecutorService scheduleTaskExecutor;

Im 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.

2

Zitieren der Planung Wiederholter Alarme - Verstehen Sie die Kompromissdokumente :

Ein häufiges Szenario zum Auslösen eines Vorgangs außerhalb der Lebensdauer Ihrer App ist das Synchronisieren von Daten mit einem Server. Dies ist ein Fall, in dem Sie möglicherweise versucht sind, einen sich wiederholenden Alarm zu verwenden. Wenn Sie jedoch den Server besitzen, auf dem die Daten Ihrer App gehostet werden, ist die Verwendung von Google Cloud Messaging (GCM) in Verbindung mit dem Synchronisierungsadapter eine bessere Lösung als AlarmManager. Ein Synchronisierungsadapter bietet Ihnen dieselben Planungsoptionen wie AlarmManager, bietet Ihnen jedoch erheblich mehr Flexibilität.

Auf dieser Grundlage können Sie einen Serveranruf am besten mithilfe von Google Cloud Messaging (GCM) in Verbindung mit dem Synchronisierungsadapter planen .


1

Ich habe eine pünktliche Aufgabe erstellt, bei der die Aufgabe, die der Benutzer wiederholen möchte, in der benutzerdefinierten timeTask run () -Methode hinzugefügt wird. es tritt erfolgreich wieder auf.

 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);
            }});
    }
}

}}

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.