Wie pausiere / schlafe Thread oder Prozess in Android?

294

Ich möchte eine Pause zwischen zwei Codezeilen machen. Lassen Sie mich ein wenig erklären:

-> Der Benutzer klickt auf eine Schaltfläche (tatsächlich eine Karte) und ich zeige sie, indem ich den Hintergrund dieser Schaltfläche ändere:

thisbutton.setBackgroundResource(R.drawable.icon);

-> Nach einer Sekunde muss ich zum vorherigen Status der Schaltfläche zurückkehren, indem ich den Hintergrund zurücksetze:

thisbutton.setBackgroundResource(R.drawable.defaultcard);

-> Ich habe versucht, den Thread zwischen diesen beiden Codezeilen anzuhalten mit:

try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

Dies funktioniert jedoch nicht. Vielleicht ist es der Prozess und nicht der Thread, den ich anhalten muss?

Ich habe es auch versucht (aber es funktioniert nicht):

new Reminder(5);

Mit diesem:

public class Reminder {

Timer timer;

        public Reminder(int seconds) {
            timer = new Timer();
            timer.schedule(new RemindTask(), seconds*1000);
        }

        class RemindTask extends TimerTask {
            public void run() {
                System.out.format("Time's up!%n");
                timer.cancel(); //Terminate the timer thread
            }
        }  
    }

Wie kann ich den Thread oder Prozess anhalten / in den Ruhezustand versetzen?

Hubert
quelle
4
Oh, benutze einfach den klassischen Thread-Pausenblock: while (true) {}
KristoferA
6
@ KristoferA-Huagati.com Ich bin mir nicht sicher, ob Sie sarkastisch sind oder ob es tatsächlich Dalvik / Android-Magie gibt, so dass dies auf Android akzeptabel ist. Können Sie bitte klarstellen? Entschuldigen Sie die Zweifel, aber ich frage, weil während (!conditionCheck()) {}normalerweise davon abgeraten wird.
Variable
"Dies funktioniert jedoch nicht." "Ich habe es auch versucht (aber es funktioniert nicht)" Dies ist ein klassisches Beispiel dafür, dass es ein Problem gibt, ohne die Symptome zu nennen. Inwiefern haben diese Versuche Ihre Anforderungen nicht erfüllt? Hat der Thread nicht angehalten? Hast du eine Fehlermeldung bekommen?
LarsH

Antworten:

454

Eine Lösung für dieses Problem ist die Verwendung der Handler.postDelayed () -Methode. Einige Google- Schulungsmaterialien schlagen dieselbe Lösung vor.

@Override
public void onClick(View v) {
    my_button.setBackgroundResource(R.drawable.icon);

    Handler handler = new Handler(); 
    handler.postDelayed(new Runnable() {
         @Override 
         public void run() { 
              my_button.setBackgroundResource(R.drawable.defaultcard); 
         } 
    }, 2000); 
}

Einige haben jedoch darauf hingewiesen, dass die obige Lösung einen Speicherverlust verursacht da sie eine nicht statische innere und anonyme Klasse verwendet, die implizit einen Verweis auf ihre äußere Klasse, die Aktivität, enthält. Dies ist ein Problem, wenn im Aktivitätskontext Müll gesammelt wird.

Eine komplexere Lösung, die den Speicherverlust vermeidet, unterteilt die Handlerund Runnablemit statischen inneren Klassen innerhalb der Aktivität, da statische innere Klassen keinen impliziten Verweis auf ihre äußere Klasse enthalten:

private static class MyHandler extends Handler {}
private final MyHandler mHandler = new MyHandler();

public static class MyRunnable implements Runnable {
    private final WeakReference<Activity> mActivity;

    public MyRunnable(Activity activity) {
        mActivity = new WeakReference<>(activity);
    }

    @Override
    public void run() {
        Activity activity = mActivity.get();
        if (activity != null) {
            Button btn = (Button) activity.findViewById(R.id.button);
            btn.setBackgroundResource(R.drawable.defaultcard);
        }
    }
}

private MyRunnable mRunnable = new MyRunnable(this);

public void onClick(View view) {
    my_button.setBackgroundResource(R.drawable.icon);

    // Execute the Runnable in 2 seconds
    mHandler.postDelayed(mRunnable, 2000);
}

Beachten Sie, dass die RunnableVerwendungen ein WeakReference auf die Aktivität, die in einer statischen Klasse notwendig ist , die den Zugriff auf die Benutzeroberfläche benötigt.

Tronman
quelle
3
Es funktioniert, aber das ist eine ziemlich unbequeme Möglichkeit, Verzögerungen im gesamten Code einzuführen, oder?
Ehtesh Choudhury
4
Ich bin mir nicht sicher, was Sie unter "unbequem" verstehen. Die postDelayed-Methode des Handlers teilt Android mit, dass nach Ablauf einer bestimmten Zeit ein Stück Code ausgeführt werden soll.
Tronman
27
nach 2 Jahren und dieser Code hat mir nur geholfen! danke @tronman !! :)
Melvin Lai
2
Sie können kopieren Sie sie einfach auf eine andere (endgültige) Variable wie so final Button mynewbutton = mybutton;und Verwendung mynewbuttonin der Handler und dem Runnable von dort weiter.
Dzhuneyt
2
@ MelvinLai nach 5 Jahren und dieser Code hat mir nur geholfen! :)
gabrieloliveira
191

Sie können dieses versuchen, es ist kurz

SystemClock.sleep(7000);

WARNUNG : Tun Sie dies niemals in einem UI-Thread.

Verwenden Sie dies zum Schlafen, z. Hintergrund-Thread.


Die vollständige Lösung für Ihr Problem lautet: Dies ist die verfügbare API 1

findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(final View button) {
                button.setBackgroundResource(R.drawable.avatar_dead);
                final long changeTime = 1000L;
                button.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        button.setBackgroundResource(R.drawable.avatar_small);
                    }
                }, changeTime);
            }
        });

Ohne tmp Handler zu erstellen. Auch diese Lösung ist besser als @tronman, da wir die Ansicht von Handler nicht beibehalten. Wir haben auch kein Problem mit dem Handler, der bei einem schlechten Thread erstellt wurde;)

Dokumentation

öffentlicher statischer Leerschlaf (lange ms)

In API-Ebene 1 hinzugefügt

Wartet eine bestimmte Anzahl von Millisekunden (von uptimeMillis), bevor Sie zurückkehren. Ähnlich wie im Schlaf (lang), löst jedoch keine InterruptedException aus . Interrupt () -Ereignisse werden bis zur nächsten unterbrechbaren Operation zurückgestellt. Wird erst zurückgegeben, wenn mindestens die angegebene Anzahl von Millisekunden verstrichen ist.

Parameter

ms vor der Rückkehr in Millisekunden Betriebszeit schlafen.

Code für postDelayed from View-Klasse:

/**
 * <p>Causes the Runnable to be added to the message queue, to be run
 * after the specified amount of time elapses.
 * The runnable will be run on the user interface thread.</p>
 *
 * @param action The Runnable that will be executed.
 * @param delayMillis The delay (in milliseconds) until the Runnable
 *        will be executed.
 *
 * @return true if the Runnable was successfully placed in to the
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.  Note that a
 *         result of true does not mean the Runnable will be processed --
 *         if the looper is quit before the delivery time of the message
 *         occurs then the message will be dropped.
 *
 * @see #post
 * @see #removeCallbacks
 */
public boolean postDelayed(Runnable action, long delayMillis) {
    final AttachInfo attachInfo = mAttachInfo;
    if (attachInfo != null) {
        return attachInfo.mHandler.postDelayed(action, delayMillis);
    }
    // Assume that post will succeed later
    ViewRootImpl.getRunQueue().postDelayed(action, delayMillis);
    return true;
}
Dawid Drozd
quelle
22
Und die Android-Benutzeroberfläche einfrieren lassen? Tu das nicht.
Shkschneider
3
Strg + Umschalt + O (Eclipse Auto Import)
Dawid Drozd
13
Gelldur, Sie verpassen den Punkt von @ RichieHHs Kommentar. Im Vergleich zu der Antwort, die 3 Jahre vor Ihrer Veröffentlichung angenommen wurde, hilft das , was Sie vorschlagen, OP nicht bei der Lösung seines Problems. Grund: Der Code wird sowohl in OP als auch in der akzeptierten Antwort in einem UI-Handler ausgeführt . Das Sprichwort OMG of course do this in background threadist irrelevant, es sei denn, Sie zeigen, wie es in den Hintergrund-Thread eingefügt werden soll. Zu diesem Zeitpunkt werden Sie feststellen, dass Sie eine Antwort haben, die komplizierter ist als die bereits akzeptierte Antwort. Habe ich erwähnt, dass vor drei Jahren eine bessere Lösung akzeptiert wurde? : P
ToolmakerSteve
2
... habe vergessen zu erwähnen, dass dieser Vorschlag für die Frage besonders unangemessen ist, dass OP nach der Verzögerung ausdrücklich UI-Aktionen ausführen möchte. Sie hätten also eine Lösung, bei der Sie einen Hintergrund-Thread erstellt haben (der bereits schwerer ist als zur Lösung des Problems erforderlich), geschlafen haben und dann (irgendwie) zum UI-Thread zurückkehren müssen. Handler + postRunnableDies alles in einem einzigen Schritt. Ohne den Systemaufwand für das Erstellen eines zweiten Threads.
ToolmakerSteve
2
@ToolmakerSteve jetzt glücklich: D? Hauptfrage ist: "Wie pausiere / schlafe Thread oder Prozess in Android?" Also war meine Antwort einfach :). Wenn jemand nach "How to Sleep Thread" googelt, wird diese Frage angezeigt. Er / Sie sucht wahrscheinlich nach meiner Antwort: P. Aber ok, ich habe die vollständige Antwort hinzugefügt;)
Dawid Drozd
28

Ich benutze das:

Thread closeActivity = new Thread(new Runnable() {
  @Override
  public void run() {
    try {
      Thread.sleep(3000);
      // Do some stuff
    } catch (Exception e) {
      e.getLocalizedMessage();
    }
  }
});
Byt3
quelle
4
Was e.getLocalizedMessage()soll der machen?
Philipp Reichart
Ich benutze e.getLocalizedMessage (), wenn ich eine einfache, allgemeine und schnelle Ausnahmemeldung benötige
Byt3
1
Aber ich wette, Sie tun dies nicht in der Situation, nach der OP innerhalb einer UI-Klickmethode fragt, die weitere Aktualisierungen an der UI vornehmen wird. Die akzeptierte Handler/postDelayedLösung hat zwei Vorteile: (1) Vermeidet den System-Overhead des 2. Threads, (1) läuft auf dem UI-Thread und kann so Änderungen an der Benutzeroberfläche vornehmen, ohne eine Ausnahme zu verursachen.
ToolmakerSteve
1
Nicht direkt mit dem Hauptziel der Frage verbunden, aber Sie sollten keine Ausnahme fangen. Fangen Sie lieber InterruptedException. Wenn Sie Exception abfangen, werden Sie alles abfangen, was schief gehen könnte, und so Probleme verbergen, die Sie sonst abfangen würden.
Herve Do
16

Sie wollen es wahrscheinlich nicht so machen. Wenn Sie eine explizite sleep()Ereignisbehandlungsroutine in Ihre Ereignisbehandlungsroutine einfügen, wird die gesamte Benutzeroberfläche für eine Sekunde gesperrt. Eine Alternative ist die Verwendung eines Single-Shot- Timers . Erstellen Sie eine TimerTask , um die Hintergrundfarbe wieder in die Standardfarbe zu ändern, und planen Sie sie im Timer.

Eine andere Möglichkeit ist die Verwendung eines Handlers . Es gibt ein Tutorial über jemanden, der von einem Timer zu einem Handler gewechselt ist.

Übrigens können Sie einen Prozess nicht anhalten. Ein Java- (oder Android-) Prozess hat mindestens 1 Thread, und Sie können nur Threads in den Ruhezustand versetzen.

Daniel Yankowsky
quelle
14

Ich benutze CountDownTime

new CountDownTimer(5000, 1000) {

    @Override
    public void onTick(long millisUntilFinished) {
        // do something after 1s
    }

    @Override
    public void onFinish() {
        // do something end times 5s
    }

}.start(); 
vudandroid
quelle
das ist nützlich.
UzaySan
9

Das habe ich am Ende des Tages getan - funktioniert jetzt einwandfrei:

@Override
    public void onClick(View v) {
        my_button.setBackgroundResource(R.drawable.icon);
        // SLEEP 2 SECONDS HERE ...
        final Handler handler = new Handler(); 
        Timer t = new Timer(); 
        t.schedule(new TimerTask() { 
                public void run() { 
                        handler.post(new Runnable() { 
                                public void run() { 
                                 my_button.setBackgroundResource(R.drawable.defaultcard); 
                                } 
                        }); 
                } 
        }, 2000); 
    }
Hubert
quelle
2
Warum nicht verzögert? Kein Timer erforderlich.
RichieHH
8

Zusätzlich zu den Antworten von Herrn Yankowsky könnten Sie auch verwenden postDelayed(). Dies ist auf jeder View(z. B. Ihrer Karte) verfügbar und dauert eine Runnableund eine Verzögerungszeit. Es führt die Runnablenach dieser Verzögerung aus.

CommonsWare
quelle
5

Das ist mein Beispiel

Erstellen Sie ein Java-Dienstprogramm

    import android.app.ProgressDialog;
    import android.content.Context;
    import android.content.Intent;

    public class Utils {

        public static void showDummyWaitingDialog(final Context context, final Intent startingIntent) {
            // ...
            final ProgressDialog progressDialog = ProgressDialog.show(context, "Please wait...", "Loading data ...", true);

            new Thread() {
                public void run() {
                    try{
                        // Do some work here
                        sleep(5000);
                    } catch (Exception e) {
                    }
                    // start next intent
                    new Thread() {
                        public void run() {
                        // Dismiss the Dialog 
                        progressDialog.dismiss();
                        // start selected activity
                        if ( startingIntent != null) context.startActivity(startingIntent);
                        }
                    }.start();
                }
            }.start();  

        }

    }    
Stefano
quelle
3
Eine weitere Antwort, die Jahre nachdem die Frage bereits gut gelöst war, hinzugefügt wurde, ist für die Frage nicht relevant, da sie sich nicht mit dem erklärten Wunsch befasst, UI-Arbeiten ausführen zu können. Sie können hier keine UI-Arbeit ausführen, da Sie einen neuen Thread ausführen. Nicht im UI-Thread.
ToolmakerSteve
1
Auf der UX-Seite ist es nicht gut, dem Benutzer einen Blockierungsdialog zum Laden von Daten anzuzeigen.
2Dee
4

Oder Sie könnten verwenden:

android.os.SystemClock.sleep(checkEvery)

Dies hat den Vorteil, dass keine Verpackung erforderlich ist try ... catch.

aaaa
quelle
0

Wenn Sie Kotlin und Coroutinen verwenden , können Sie dies einfach tun

GlobalScope.launch {
   delay(3000) // In ms
   //Code after sleep
}

Und wenn Sie die Benutzeroberfläche aktualisieren müssen

GlobalScope.launch {
  delay(3000)
  GlobalScope.launch(Dispatchers.Main) {
    //Action on UI thread
  }
}
Guillaume
quelle
0

Ich weiß, dass dies ein alter Thread ist, aber in der Android-Dokumentation habe ich eine Lösung gefunden, die für mich sehr gut funktioniert hat ...

new CountDownTimer(30000, 1000) {

    public void onTick(long millisUntilFinished) {
        mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
    }

    public void onFinish() {
        mTextField.setText("done!");
    }
}.start();

https://developer.android.com/reference/android/os/CountDownTimer.html

Hoffe das hilft jemandem ...

John Byrne
quelle
0
  class MyActivity{
    private final Handler handler = new Handler();
    private Runnable yourRunnable;
    protected void onCreate(@Nullable Bundle savedInstanceState) {
       // ....
       this.yourRunnable = new Runnable() {
               @Override
               public void run() {
                   //code
               }
            };

        this.handler.postDelayed(this.yourRunnable, 2000);
       }


     @Override
  protected void onDestroy() {
      // to avoid memory leaks
      this.handler.removeCallbacks(this.yourRunnable);
      }
    }

Und um doppelt sicher zu sein, können Sie es mit der Methode "static class" kombinieren, wie in der Antwort von tronman beschrieben

Anfänger
quelle