Wie führe ich einen Runnable-Thread in Android in definierten Intervallen aus?

351

Ich habe eine Anwendung entwickelt, mit der Text in definierten Intervallen auf dem Android-Emulatorbildschirm angezeigt werden kann. Ich benutze die HandlerKlasse. Hier ist ein Ausschnitt aus meinem Code:

handler = new Handler();
Runnable r = new Runnable() {
    public void run() {
        tv.append("Hello World");               
    }
};
handler.postDelayed(r, 1000);

Wenn ich diese Anwendung ausführe, wird der Text nur einmal angezeigt. Warum?

Rajapandian
quelle
109
Ich kann mich nie erinnern, wie man ein Runnable macht, also besuche ich immer deinen Beitrag darüber, wie es geht :))
Adrian Sicaru
2
haha gleich hier Kumpel so wahr
NoXSaeeD
1
Lambdas sind jetzt die meiste Zeit der richtige Weg;)
Xerus
@AdrianSicaru: das gleiche
Sovandara LENG

Antworten:

534

Die einfache Lösung für Ihr Beispiel lautet:

handler = new Handler();

final Runnable r = new Runnable() {
    public void run() {
        tv.append("Hello World");
        handler.postDelayed(this, 1000);
    }
};

handler.postDelayed(r, 1000);

Oder wir können zum Beispiel einen normalen Thread verwenden (mit dem ursprünglichen Runner):

Thread thread = new Thread() {
    @Override
    public void run() {
        try {
            while(true) {
                sleep(1000);
                handler.post(this);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
};

thread.start();

Sie können Ihr ausführbares Objekt nur als Befehl betrachten, der zur Ausführung an die Nachrichtenwarteschlange gesendet werden kann, und den Handler nur als Hilfsobjekt, das zum Senden dieses Befehls verwendet wird.

Weitere Details finden Sie hier http://developer.android.com/reference/android/os/Handler.html

alex2k8
quelle
Alex, ich habe einen kleinen Zweifel. Jetzt läuft der Thread perfekt und zeigt den Text kontinuierlich an. Wenn ich aufhören möchte, bedeutet dies, was ich tun muss? Bitte helfen Sie mir.
Rajapandian
11
Sie können die boolesche Variable _stop definieren und sie auf 'true' setzen, wenn Sie anhalten möchten. Und ändern Sie 'while (true)' in 'while (! _ Stop)', oder wenn das erste Beispiel verwendet wird, ändern Sie einfach in 'if (! _ Stop) handler.postDelayed (this, 1000)'.
Alex2k8
Was ist, wenn ich die Nachricht neu starten möchte?
Sonhja
und wenn ich eine ausführbare Datei benötige, um 8 verschiedene ImageViews nacheinander sichtbar zu machen, dann setze ich sie alle auf die gleiche Weise unsichtbar und so weiter (um eine "blinkende" Animation zu erstellen), wie kann ich das tun?
Droidman
1
Wenn Sie sicherstellen möchten, dass der Handler an den Hauptthread angehängt wird, sollten Sie ihn wie folgt initialisieren: handler = new Handler (Looper.getMainLooper ());
Yair Kukielka
47
new Handler().postDelayed(new Runnable() {
    public void run() {
        // do something...              
    }
}, 100);
user2212515
quelle
2
Wenn Sie sicherstellen möchten, dass der Handler an den Hauptthread angehängt wird, sollten Sie ihn folgendermaßen initialisieren: new Handler (Looper.getMainLooper ());
Yair Kukielka
1
Entspricht diese Lösung nicht dem ursprünglichen Beitrag? Runnable wird nach 100 Millisekunden nur einmal ausgeführt.
Tronman
@ YairKukielka Antwort ist die Lösung! Sie müssen MainLooper anhängen. so ein Lebensretter!
Houssem Chlegou
40

Ich denke, kann erste Lösung von Alex2k8 für Update jede Sekunde richtig verbessern

1. Originalcode:

public void run() {
    tv.append("Hello World");
    handler.postDelayed(this, 1000);
}

2. Analyse

  • Nehmen Sie bei den oben genannten Kosten an, dass die tv.append("Hello Word")Kosten T Millisekunden betragen, nachdem die 500- fach verzögerte Anzeigezeit 500 * T Millisekunden beträgt
  • Es wird sich verzögert erhöhen, wenn es lange läuft

3. Lösung

Um dies zu vermeiden, ändern Sie einfach die Reihenfolge von postDelayed (), um Verzögerungen zu vermeiden:

public void run() {
    handler.postDelayed(this, 1000);
    tv.append("Hello World");
}
NguyenDat
quelle
6
-1 Sie gehen davon aus, dass die Aufgabe, die Sie in run () ausführen, einen konstanten Betrag pro Lauf kostet. Wenn dies eine Operation für dynamische Daten wäre (was normalerweise der Fall ist), würden Sie am Ende mehr als einen run () ausführen Einmal. Aus diesem Grund wird postDelayed normalerweise am Ende platziert.
Jay
1
@ Jay Leider liegst du falsch. Ein Handler ist einem einzelnen Thread (und einem Looper, der die Ausführungsmethode dieses Threads darstellt) + einer MessageQueue zugeordnet. Jedes Mal, wenn Sie eine Nachricht veröffentlichen, stellen Sie sie in die Warteschlange und wenn der Looper das nächste Mal die Warteschlange überprüft, führt er die Ausführungsmethode der von Ihnen veröffentlichten Runnable aus. Da dies alles in nur einem Thread geschieht, können Sie nicht mehr als einen Thread gleichzeitig ausführen lassen. Wenn Sie zuerst postDelayed ausführen, kommen Sie 1000 ms pro Ausführung näher, da intern die aktuelle Zeit + 1000 als Ausführungszeit verwendet wird. Wenn Sie Code vor den Beitrag stellen, fügen Sie eine zusätzliche Verzögerung hinzu.
Zapl
1
@zapl danke für den Tipp zum Handler, ich ging davon aus, dass er mehrere Runnables und damit mehrere Threads ausführen würde. Intern funktioniert eine Bedingung wie ((aktuelle Zeit - Lastruntime)> 1000) einwandfrei, wenn die Laufdauer kleiner oder gleich 1000 ms ist. Wenn diese jedoch überschritten wird, tritt der Timer sicherlich in nichtlinearen Intervallen auf hängt vollständig von der Ausführungszeit der Ausführungsmethode ab (daher mein Punkt zu unvorhersehbaren Rechenkosten)
Jay
Wenn Sie einen festen Zeitraum ohne Konflikt wünschen, messen Sie die Startzeit vor der Arbeit und passen Sie Ihre Verzögerung entsprechend an. Sie werden immer noch eine kleine Latenz sehen, wenn die CPU ausgelastet ist, aber es kann Ihnen eine strengere Zeitspanne ermöglichen und Sie können erkennen, ob das System überlastet ist (möglicherweise um zu signalisieren, dass Dinge mit niedriger Priorität zurückgesetzt werden sollen).
Ajax
27

Zum Wiederholen von Aufgaben können Sie verwenden

new Timer().scheduleAtFixedRate(task, runAfterADelayForFirstTime, repeaingTimeInterval);

nenne es wie

new Timer().scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {

            }
        },500,1000);

Der obige Code wird zum ersten Mal nach einer halben Sekunde (500) ausgeführt und wiederholt sich nach jeder Sekunde (1000).

Wo

Aufgabe ist die auszuführende Methode

nach der Zeit bis zur ersten Ausführung

( Intervall die Zeit für die Wiederholung der Ausführung)

Zweitens

Sie können CountDownTimer auch verwenden, wenn Sie eine Aufgabe mehrmals ausführen möchten.

    new CountDownTimer(40000, 1000) { //40000 milli seconds is total time, 1000 milli seconds is time interval

     public void onTick(long millisUntilFinished) {
      }
      public void onFinish() {
     }
    }.start();

//Above codes run 40 times after each second

Und Sie können es auch mit runnable tun. Erstellen Sie eine ausführbare Methode wie

Runnable runnable = new Runnable()
    {
        @Override
        public void run()
        {

        }
    };

Und nenne es auf beide Arten

new Handler().postDelayed(runnable, 500 );//where 500 is delayMillis  // to work on mainThread

ODER

new Thread(runnable).start();//to work in Background 
Zar E Ahmer
quelle
Wie kann ich für Option 3 pausieren / fortsetzen und auch dauerhaft anhalten?
Si8
Erstellen Sie eine Instanz von Handler wie Handler handler = new Handler () und entfernen Sie sie wie handler.removeCallbacksAndMessages (null);
Zar E Ahmer
24

Ich halte es für diesen typischen Fall für Timerangemessener, etwas mit einem festen Intervall auszuführen . Hier ist ein einfaches Beispiel:

myTimer = new Timer();
myTimer.schedule(new TimerTask() {          
@Override
public void run() {
    // If you want to modify a view in your Activity
    MyActivity.this.runOnUiThread(new Runnable()
        public void run(){
            tv.append("Hello World");
        });
    }
}, 1000, 1000); // initial delay 1 second, interval 1 second

Die Verwendung Timerhat nur wenige Vorteile:

  • Die anfängliche Verzögerung und das Intervall können einfach in den scheduleFunktionsargumenten angegeben werden
  • Der Timer kann durch einfaches Aufrufen gestoppt werden myTimer.cancel()
  • Wenn nur ein Thread ausgeführt werden soll, denken Sie daran, myTimer.cancel() vor dem Planen eines neuen Threads aufzurufen (wenn myTimer nicht null ist).
iTech
quelle
7
Ich glaube nicht, dass ein Timer angemessener ist, da er den Android-Lebenszyklus nicht berücksichtigt. Wenn Sie pausieren und fortfahren, gibt es keine Garantie dafür, dass der Timer ordnungsgemäß ausgeführt wird. Ich würde argumentieren, dass ein Runnable die bessere Wahl ist.
Janpan
1
Bedeutet das, dass ein Handler angehalten wird, wenn eine App in den Hintergrund gestellt wird? und wenn es wieder fokussiert wird, geht es weiter (mehr oder weniger), als wäre nichts passiert?
Andrew Gallasch
17
Handler handler=new Handler();
Runnable r = new Runnable(){
    public void run() {
        tv.append("Hello World");                       
        handler.postDelayed(r, 1000);
    }
}; 
handler.post(r);
singh arjun
quelle
5
Dies sollte einen Fehler ergeben. In Ihrer zweiten Zeile rufen Sie eine Variable auf, rdie noch definiert ist.
Seoul
Wenn Sie sicherstellen möchten, dass der Handler an den Hauptthread angehängt wird, sollten Sie ihn wie folgt initialisieren: handler = new Handler (Looper.getMainLooper ());
Yair Kukielka
nur wiederholte Antwort!
Hamid
Wie kann ich die Ausführung mit einem Klick auf die Bildansicht anhalten / fortsetzen?
Si8
4

Wenn ich die Dokumentation der Handler.post () -Methode richtig verstehe:

Bewirkt, dass Runnable r zur Nachrichtenwarteschlange hinzugefügt wird. Die ausführbare Datei wird auf dem Thread ausgeführt, an den dieser Handler angehängt ist.

Die von @ alex2k8 bereitgestellten Beispiele sind also nicht dieselben, obwohl sie ordnungsgemäß funktionieren. Falls Handler.post()verwendet, werden keine neuen Threads erstellt . Sie posten einfach Runnablein den Thread Handler, der von EDT ausgeführt werden soll . Danach wird EDT nur noch ausgeführt Runnable.run(), sonst nichts.

Denken Sie daran : Runnable != Thread.

Damian Walczak
quelle
1
Stimmt das. Erstellen Sie niemals jedes Mal einen neuen Thread. Der Sinn von Handler und anderen Ausführungspools besteht darin, dass ein oder zwei Threads Aufgaben aus einer Warteschlange ziehen, um Thread-Erstellung und GC zu vermeiden. Wenn Sie eine wirklich undichte App haben, kann der zusätzliche GC helfen, OutOfMemory-Situationen zu vertuschen. In beiden Fällen ist es jedoch besser, nicht mehr Arbeit zu erstellen, als Sie benötigen.
Ajax
Ein besserer Weg, dies zu tun, ist die Verwendung des normalen Threads basierend auf der Antwort von alex2k8?
Compaq LE2202x
4

Kotlin

private lateinit var runnable: Runnable
override fun onCreate(savedInstanceState: Bundle?) {
    val handler = Handler()
    runnable = Runnable {
        // do your work
        handler.postDelayed(runnable, 2000)
    }
    handler.postDelayed(runnable, 2000)
}

Java

Runnable runnable;
Handler handler;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    handler = new Handler();
    runnable = new Runnable() {
        @Override
        public void run() {
            // do your work
            handler.postDelayed(this, 1000);
        }
    };
    handler.postDelayed(runnable, 1000);
}
Khemraj
quelle
1

Ein interessantes Beispiel ist, dass Sie kontinuierlich einen Zähler / eine Stoppuhr in einem separaten Thread sehen können. Zeigt auch GPS-Standort an. Während der Hauptaktivität ist der Benutzeroberflächenthread bereits vorhanden.

Auszug:

try {    
    cnt++; scnt++;
    now=System.currentTimeMillis();
    r=rand.nextInt(6); r++;    
    loc=lm.getLastKnownLocation(best);    

    if(loc!=null) { 
        lat=loc.getLatitude();
        lng=loc.getLongitude(); 
    }    

    Thread.sleep(100); 
    handler.sendMessage(handler.obtainMessage());
} catch (InterruptedException e) {   
    Toast.makeText(this, "Error="+e.toString(), Toast.LENGTH_LONG).show();
}

Um den Code anzusehen, siehe hier:

Thread-Beispiel zur Anzeige des GPS-Standorts und der aktuellen Uhrzeit, die neben dem Benutzeroberflächenthread der Hauptaktivität ausgeführt werden können

Animesh Shrivastav
quelle
1
Tipp: Wenn Sie Ihre Antwort nützlich machen möchten, lernen Sie hier, wie Sie Eingaben formatieren. Dieses Vorschaufenster existiert aus einem bestimmten Grund.
GhostCat
0

Jetzt können Sie in Kotlin Threads folgendermaßen ausführen:

class SimpleRunnable: Runnable {
    public override fun run() {
        println("${Thread.currentThread()} has run.")
    }
}
fun main(args: Array<String>) {
    val thread = SimpleThread()
    thread.start() // Will output: Thread[Thread-0,5,main] has run.
    val runnable = SimpleRunnable()
    val thread1 = Thread(runnable)
    thread1.start() // Will output: Thread[Thread-1,5,main] has run
}
André Abboud
quelle
0

Kotlin mit Coroutinen

In Kotlin können Sie mit Coroutinen Folgendes tun:

CoroutineScope(Dispatchers.Main).launch { // Main, because UI is changed
    ticker(delayMillis = 1000, initialDelayMillis = 1000).consumeEach {
        tv.append("Hello World")
    }
}

Probieren Sie es hier aus !

Willi Mentzel
quelle