Come mettere in pausa / sospendere il thread o elaborare in Android?


294

Voglio fare una pausa tra due righe di codice, lasciami spiegare un po ':

-> l'utente fa clic su un pulsante (una scheda in effetti) e lo faccio vedere cambiando lo sfondo di questo pulsante:

thisbutton.setBackgroundResource(R.drawable.icon);

-> dopo un secondo, devo tornare allo stato precedente del pulsante cambiando il suo sfondo:

thisbutton.setBackgroundResource(R.drawable.defaultcard);

-> Ho provato a mettere in pausa il thread tra queste due righe di codice con:

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

Tuttavia, questo non funziona. Forse è il processo e non il thread che devo mettere in pausa?

Ho anche provato (ma non funziona):

new Reminder(5);

Con questo:

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

Come posso mettere in pausa / mettere in pausa il thread o l'elaborazione?


4
Oh, basta usare il classico blocco di pausa del thread: while (true) {}
KristoferA

6
@ KristoferA-Huagati.com Non sono sicuro che tu sia sarcastico o che ci sia un po 'di magia Dalvik / Android, quindi questo è accettabile su Android. Potete per favore chiarire? Scusate il dubbio, ma chiedo perché mentre di (!conditionCheck()) {}solito è scoraggiato.
Miserabile variabile,

"Tuttavia, questo non funziona." "Ho anche provato (ma non funziona)" Questo è un classico esempio di dire che c'è un problema senza dare i sintomi. In che modo questi tentativi non hanno soddisfatto le tue esigenze? Il thread non si è interrotto? Hai ricevuto un messaggio di errore?
LarsH,

Risposte:


454

Una soluzione a questo problema è utilizzare il metodo Handler.postDelayed () . Alcuni materiali di formazione di Google suggeriscono la stessa soluzione.

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

Tuttavia, alcuni hanno sottolineato che la soluzione di cui sopra provoca una perdita di memoria perché utilizza una classe interna e anonima non statica che contiene implicitamente un riferimento alla sua classe esterna, l'attività. Questo è un problema quando il contesto dell'attività è garbage collection.

Una soluzione più complessa che evita la perdita di memoria sottoclassi la Handlere Runnablecon classi interne statiche all'interno dell'attività dal classi interne statiche non contenere un implicito riferimento alla loro classe esterna:

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

Si noti che Runnableutilizza un WeakReference per l'attività, che è necessario in una classe statica che deve accedere all'interfaccia utente.


3
Funziona, ma è un modo abbastanza scomodo di introdurre ritardi nel codice, giusto?
Ehtesh Choudhury,

4
Non sono sicuro di cosa intendi per "inconveniente". Il metodo PostDelayed del gestore è progettato per indicare ad Android che si desidera eseguire un po 'di codice dopo un certo periodo di tempo.
tronman,

27
dopo 2 anni e questo codice mi ha appena aiutato! grazie @tronman !! :)
Melvin Lai,

2
Puoi semplicemente copiarlo in un'altra (finale) variabile in questo modo final Button mynewbutton = mybutton;e utilizzarlo mynewbuttonnel Gestore e nel Runnable da lì in poi.
Dzhuneyt,

2
@MelvinLai dopo 5 anni e questo codice mi ha aiutato! :)
gabrieloliveira,

191

Puoi provare questo è breve

SystemClock.sleep(7000);

ATTENZIONE : mai e poi mai farlo su un thread dell'interfaccia utente.

Usa questo per dormire ad es. filo di fondo.


La soluzione completa per il tuo problema sarà: questa è l'API 1 disponibile

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

Senza creare il gestore tmp. Anche questa soluzione è migliore di @tronman perché non conserviamo la vista di Handler. Inoltre non abbiamo problemi con Handler creato con thread non validi;)

Documentazione

sleep vuoto statico pubblico (long ms)

Aggiunto nel livello API 1

Attende un determinato numero di millisecondi (di uptimeMillis) prima di tornare. Simile a sleep (long), ma non genera InterruptedException ; gli eventi interrupt () vengono rinviati alla successiva operazione interrompibile. Non restituisce fino a quando non è trascorso almeno il numero di millisecondi specificato.

parametri

ms per dormire prima di tornare, in millisecondi di uptime.

Codice per post Ritardato dalla classe View:

/**
 * <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;
}

22
E lasciare che l'interfaccia utente Android si blocchi? Non farlo
shkschneider,

3
Ctrl + Maiusc + O (importazione automatica Eclipse)
Dawid Drozd,

13
Gelldur, ti manca il punto del commento di @ RichieHH. Rispetto alla risposta che è stata accettata 3 anni prima della pubblicazione , ciò che suggerisci non aiuta OP a risolvere il suo problema. Motivo: il codice, sia come mostrato da OP, sia come mostrato nella risposta accettata, è in esecuzione all'interno di un gestore UI . Dire OMG of course do this in background threadè irrilevante, a meno che tu non mostri COME metterlo nel thread in background. A quel punto, scoprirai di avere una risposta più complicata della risposta già accettata. Ho già detto che una soluzione migliore è stata accettata tre anni fa? : P
ToolmakerSteve

2
... ho dimenticato di menzionare che ciò che rende questo suggerimento particolarmente sconsiderato alla domanda, è che OP vuole esplicitamente fare un'azione UI dopo il ritardo. Quindi, avresti una soluzione in cui hai creato un thread in background (già più pesante di quanto sia necessario per risolvere il problema), dormito e quindi devi (in qualche modo) tornare al thread dell'interfaccia utente. Handler + postRunnablecompie tutto questo, in un solo passaggio. Senza il sovraccarico del sistema di creare un secondo thread.
ToolmakerSteve,

2
@ToolmakerSteve felice ora: D? La domanda principale è: "Come mettere in pausa / sospendere il thread o elaborare in Android?" quindi la mia risposta è stata semplice :). Se qualcuno google per "come dormire thread" questa domanda mostrerà. Probabilmente sta cercando la mia risposta: P. Ma ok ho aggiunto la risposta completa;)
Dawid Drozd,

28

Io lo uso questo:

Thread closeActivity = new Thread(new Runnable() {
  @Override
  public void run() {
    try {
      Thread.sleep(3000);
      // Do some stuff
    } catch (Exception e) {
      e.getLocalizedMessage();
    }
  }
});

4
Cosa e.getLocalizedMessage()dovrebbe fare?
Philipp Reichart,

utilizzo e.getLocalizedMessage () quando ho bisogno di un messaggio di eccezione semplice, generale e rapido
Byt3

1
Ma scommetto che non lo fai nella situazione che OP richiede, all'interno di un metodo di clic dell'interfaccia utente, che farà ulteriori aggiornamenti all'interfaccia utente. La Handler/postDelayedsoluzione accettata presenta due vantaggi: (1) evita il sovraccarico del sistema del secondo thread, (1) viene eseguito sul thread dell'interfaccia utente, quindi può apportare modifiche all'interfaccia utente senza causare un'eccezione.
ToolmakerSteve,

1
Non direttamente correlato all'obiettivo principale della domanda, ma non dovresti prendere l'eccezione. Prendi piuttosto InterruptedException. Catturando l'eccezione, scoprirai tutto ciò che potrebbe andare storto, nascondendo così i problemi che altrimenti potresti rilevare.
Herve gio

16

Probabilmente non vorrai farlo in questo modo. Inserendo un esplicito sleep()nel gestore di eventi con clic sul pulsante, si bloccherebbe effettivamente l'intera IU per un secondo. Un'alternativa è usare una sorta di timer a colpo singolo . Creare un TimerTask per riportare il colore di sfondo al colore predefinito e programmarlo sul Timer.

Un'altra possibilità è quella di utilizzare un gestore . C'è un tutorial su qualcuno che è passato dall'uso del timer all'utilizzo di un gestore.

Per inciso, non è possibile mettere in pausa un processo. Un processo Java (o Android) ha almeno 1 thread e puoi solo dormire thread.


14

Uso 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(); 

questo è utile.
UzaySan

9

Questo è quello che ho fatto alla fine della giornata - funziona bene ora:

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

2
Perché non post ritardato? Nessun timer richiesto.
RichieHH,

8

Oltre alle risposte di Mr. Yankowsky, potresti anche usare postDelayed(). Questo è disponibile su qualsiasi View(ad esempio, la tua carta) e richiede un Runnablee un periodo di ritardo. Esegue il Runnableritardo successivo.


5

Questo è il mio esempio

Crea un Java Utils

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

        }

    }    

3
Ancora un'altra risposta aggiunta anni dopo che la domanda era già ben risolta, che non è rilevante per la domanda, perché non affronta il desiderio dichiarato di essere in grado di eseguire il lavoro dell'interfaccia utente. Non puoi far funzionare l'interfaccia utente qui, perché stai eseguendo un nuovo thread. Non sul thread dell'interfaccia utente.
ToolmakerSteve,

1
Sul lato UX delle cose, mostrare una finestra di dialogo di blocco all'utente per caricare i dati non è ... buono.
2Dee,

4

Oppure potresti usare:

android.os.SystemClock.sleep(checkEvery)

che ha il vantaggio di non richiedere un avvolgimento try ... catch.


0

Se usi Kotlin e coroutine , puoi semplicemente farlo

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

E se è necessario aggiornare l'interfaccia utente

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

0

So che questo è un vecchio thread, ma nella documentazione di Android ho trovato una soluzione che ha funzionato molto bene per me ...

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

Spero che questo aiuti qualcuno ...


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

E per essere doppiamente sicuro, puoi combinarlo con il metodo della "classe statica" come descritto nella risposta tronman

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.