¿Cómo pausar / dormir hilo o proceso en Android?

294

Quiero hacer una pausa entre dos líneas de código. Permítanme explicar un poco:

-> el usuario hace clic en un botón (una tarjeta de hecho) y lo muestro cambiando el fondo de este botón:

thisbutton.setBackgroundResource(R.drawable.icon);

-> después de decir 1 segundo, necesito volver al estado anterior del botón volviendo a cambiar su fondo:

thisbutton.setBackgroundResource(R.drawable.defaultcard);

-> He tratado de pausar el hilo entre estas dos líneas de código con:

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

Sin embargo, esto no funciona. ¿Tal vez es el proceso y no el hilo el que necesito pausar?

También he intentado (pero no funciona):

new Reminder(5);

Con este:

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

¿Cómo puedo pausar / suspender el hilo o proceso?

Hubert
fuente
44
Oh, solo use el clásico bloque de pausa de hilo: while (verdadero) {}
KristoferA
66
@ KristoferA-Huagati.com No estoy seguro de si estás siendo sarcástico o si hay algo de magia Dalvik / Android para que esto sea aceptable en Android. ¿Puedes por favor aclarar? Perdón por dudar, pero pregunto porque, aunque por (!conditionCheck()) {}lo general se desalienta.
Variable miserable
"Sin embargo, esto no funciona". "También lo intenté (pero no funciona)" Este es un ejemplo clásico de decir que hay un problema sin dar los síntomas. ¿De qué manera estos intentos no cumplieron con sus requisitos? ¿El hilo no se detuvo? ¿Recibió un mensaje de error?
LarsH

Respuestas:

454

Una solución a este problema es usar el método Handler.postDelayed () . Algunos materiales de capacitación de Google sugieren la misma solución.

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

Sin embargo, algunos han señalado que la solución anterior causa una pérdida de memoria porque utiliza una clase interna y anónima no estática que implícitamente contiene una referencia a su clase externa, la actividad. Este es un problema cuando el contexto de actividad es recolección de basura.

Una solución más compleja que evita la pérdida de memoria subclases Handlery Runnablecon clases internas estáticas dentro de la actividad, ya que las clases internas estáticas no tienen una referencia implícita a su clase externa:

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

Tenga en cuenta que Runnableutiliza una referencia débil a la actividad, que es necesaria en una clase estática que necesita acceso a la interfaz de usuario.

tronman
fuente
3
Funciona, pero esa es una forma bastante inconveniente de introducir demoras en todo el código, ¿verdad?
Ehtesh Choudhury
44
No estoy seguro de lo que quieres decir con "inconveniente". El método postDelayed del Handler está diseñado para decirle a Android que desea que se ejecute un poco de código después de que haya pasado un cierto tiempo.
tronman
27
¡Después de 2 años y este código me ayudó! gracias @tronman !! :)
Melvin Lai
2
Simplemente puede copiarlo a otra variable (final) como tal final Button mynewbutton = mybutton;y usarlo mynewbuttonen el controlador y el ejecutable a partir de ahí.
Dzhuneyt
2
¡@MelvinLai después de 5 años y este código me ayudó! :)
gabrieloliveira
191

Puedes probar este es corto

SystemClock.sleep(7000);

ADVERTENCIA : Nunca, nunca, haga esto en un subproceso de interfaz de usuario.

Use esto para dormir, por ejemplo. hilo de fondo.


La solución completa para su problema será: Esta está disponible 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);
            }
        });

Sin crear tmp Handler. Además, esta solución es mejor que @tronman porque no conservamos la vista de Handler. Además, no tenemos problemas con el controlador creado en el subproceso incorrecto;)

Documentación

sueño vacío público estático (ms largo)

Agregado en API nivel 1

Espera un número determinado de milisegundos (de uptimeMillis) antes de regresar. Similar a dormir (largo), pero no arroja InterruptedException ; Los eventos interrupt () se difieren hasta la siguiente operación interrumpible. No regresa hasta que haya transcurrido al menos el número especificado de milisegundos.

Parámetros

ms para dormir antes de regresar, en milisegundos de tiempo de actividad.

Código para postDelayed de la clase 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;
}
Dawid Drozd
fuente
22
¿Y dejar que la interfaz de usuario de Android se congele? No hagas esto.
shkschneider
3
Ctrl + Shift + O (importación automática de Eclipse)
Dawid Drozd
13
Gelldur, te estás perdiendo el punto del comentario de @ RichieHH. En comparación con la respuesta que fue aceptada 3 años antes de publicar , lo que sugiere no ayuda a OP a resolver su problema. Motivo: el código, tanto como se muestra en OP, como en la respuesta aceptada, se está ejecutando dentro de un controlador de IU . Decir OMG of course do this in background threades irrelevante, a menos que muestre CÓMO ponerlo en el hilo de fondo. En ese momento, descubrirá que tiene una respuesta más complicada que la respuesta ya aceptada. ¿Mencioné que se aceptó una solución mejor hace tres años? : P
ToolmakerSteve
2
... olvidé mencionar que lo que hace que esta sugerencia sea particularmente fuera de propósito a la pregunta, es que OP quiere explícitamente hacer una acción de IU después del retraso. Por lo tanto, tendría una solución en la que ha creado un subproceso de fondo (ya es más pesado de lo necesario para resolver el problema), durmió y luego debe (de alguna manera) volver al subproceso de la interfaz de usuario. Handler + postRunnablelogra todo esto, en un solo paso. Sin la sobrecarga del sistema de crear un segundo hilo.
ToolmakerSteve
2
@ToolmakerSteve feliz ahora: D? La pregunta principal es: "¿Cómo pausar / suspender el hilo o el proceso en Android?" entonces mi respuesta fue simple :). Si alguien busca en Google "cómo dormir hilo", se mostrará esta pregunta. Probablemente esté buscando mi respuesta: P. Pero bien, agregué una respuesta completa;)
Dawid Drozd
28

Yo uso esto:

Thread closeActivity = new Thread(new Runnable() {
  @Override
  public void run() {
    try {
      Thread.sleep(3000);
      // Do some stuff
    } catch (Exception e) {
      e.getLocalizedMessage();
    }
  }
});
Byt3
fuente
44
¿Qué se e.getLocalizedMessage()supone que debe hacer?
Philipp Reichart
uso e.getLocalizedMessage () cuando necesito un mensaje de excepción simple, general y rápido
Byt3
1
Pero apuesto a que no hace esto en la situación sobre la que OP pregunta, dentro de un método de clic en la interfaz de usuario, que hará más actualizaciones a la interfaz de usuario. La Handler/postDelayedsolución aceptada tiene dos ventajas: (1) evita la sobrecarga del sistema del segundo subproceso, (1) se ejecuta en el subproceso de la interfaz de usuario, por lo que puede realizar cambios en la interfaz de usuario sin causar una excepción.
ToolmakerSteve
1
No está directamente relacionado con el objetivo principal de la pregunta, pero no debe detectar la excepción. Más bien atrapar InterruptedException. Al capturar Exception, detectará todo lo que podría salir mal, ocultando así los problemas que de otro modo se detectarían.
Herve jue
16

Probablemente no quieras hacerlo de esa manera. Al poner un código explícito sleep()en el controlador de eventos con el botón presionado, en realidad bloquearía toda la interfaz de usuario por un segundo. Una alternativa es utilizar algún tipo de temporizador de disparo único . Cree una TimerTask para volver a cambiar el color de fondo al color predeterminado y programarlo en el Timer.

Otra posibilidad es usar un controlador . Hay un tutorial sobre alguien que cambió de usar un temporizador a usar un controlador.

Por cierto, no puede pausar un proceso. Un proceso Java (o Android) tiene al menos 1 subproceso, y solo puede dormir subprocesos.

Daniel Yankowsky
fuente
14

Yo 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(); 
vudandroid
fuente
esto es útil.
UzaySan
9

Esto es lo que hice al final del día: funciona bien ahora:

@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
fuente
2
¿Por qué no postDelayed? No se requiere temporizador.
RichieHH
8

Además de las respuestas del Sr. Yankowsky, también puede usar postDelayed(). Está disponible en cualquier View(p. Ej., Su tarjeta) y requiere un Runnableperíodo de retraso. Se ejecuta Runnabledespués de ese retraso.

CommonsWare
fuente
5

Este es mi ejemplo

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

        }

    }    
Stefano
fuente
3
Otra respuesta añadida años después de que la pregunta ya estaba bien resuelta, eso no es relevante para la pregunta, porque no trata con el deseo declarado de poder realizar el trabajo de la interfaz de usuario. No puede hacer que la interfaz de usuario funcione aquí, porque se está ejecutando en un nuevo hilo. No en el hilo de la interfaz de usuario.
ToolmakerSteve
1
En el lado de UX, mostrar un diálogo de bloqueo para que el usuario cargue datos ... no es bueno.
2Dee
4

O podrías usar:

android.os.SystemClock.sleep(checkEvery)

que tiene la ventaja de no requerir envoltura try ... catch.

aaaa
fuente
0

Si usa Kotlin y corutinas , simplemente puede hacer

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

Y si necesita actualizar la interfaz de usuario

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

Sé que este es un hilo viejo, pero en la documentación de Android encontré una solución que funcionó muy bien para mí ...

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

Espero que esto ayude a alguien...

John Byrne
fuente
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);
      }
    }

Y para estar seguro de que se puede combinar con el método de "clase estática" como se describe en la respuesta de tronman

principiante
fuente