Cómo llamar a un método después de un retraso en Android


770

Quiero poder llamar al siguiente método después de un retraso especificado. En el objetivo c había algo como:

[self performSelector:@selector(DoSomething) withObject:nil afterDelay:5];

¿Hay un equivalente de este método en Android con Java? Por ejemplo, necesito poder llamar a un método después de 5 segundos.

public void DoSomething()
{
     //do something here
}

Respuestas:


1859

Kotlin

Handler().postDelayed({
  //Do something after 100ms
}, 100)


Java

final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
  @Override
  public void run() {
    //Do something after 100ms
  }
}, 100);



109
Esta solución solo es útil en el subproceso de la interfaz de usuario. De lo contrario, en el subproceso normal, debe implementar looper, que no es la mejor versión, creo
olivier_sdg

2
@olivier_sdg ¿por qué necesita implementar looper?
djechlin

37
@djechlin Un controlador siempre debe estar vinculado a un Looper, que en realidad procesará el Runnable que publicas (). El hilo de la interfaz de usuario ya viene con un Looper, por lo que puede crear un nuevo controlador () en el hilo de la interfaz de usuario y publicar () Runnables directamente en él. Estos Runnables se ejecutan en el hilo de la interfaz de usuario. Para ejecutar Runnables en otro subproceso, debe crear un nuevo subproceso, luego Looper.prepare (), crear un nuevo controlador () y luego Looper.loop (). Cualquier Runnables publicado en este nuevo controlador se ejecutará en este nuevo hilo. Si no haces todo esto, la publicación () arrojará una excepción.
Dororo

12
En caso de que lo necesite, también puede cancelar la ejecución siempre que el Runnable aún esté en la cola de mensajes llamando removeCallbacks(Runnable r)al Handler.
Dennis

99
deberíaimport android.os.handler
KaKa

322

No pude usar ninguna de las otras respuestas en mi caso. Utilicé el temporizador nativo de Java en su lugar.

new Timer().schedule(new TimerTask() {          
    @Override
    public void run() {
        // this code will be executed after 2 seconds       
    }
}, 2000);

43
esto es mejor que los que usan el controlador, porque no tiene problemas de Looper cuando el controlador no se ejecuta en el hilo de la interfaz de usuario.
Ben H

32
Debe mantener una referencia a su temporizador para cancelarlo cuando ya no lo necesite, ya que según el documento de Android: "Cuando ya no se necesita un temporizador, los usuarios deben llamar a cancel (), que libera el hilo del temporizador y otros recursos. Los temporizadores no cancelados explícitamente pueden retener recursos indefinidamente ".
Pooks

14
¡Atención! Esto no se ejecuta en el hilo de la interfaz de usuario. Ejecutar esto en el hilo de la interfaz de usuario causó un error fatal: android.view.ViewRootImpl $ CalledFromWrongThreadException: solo el hilo original que creó una jerarquía de vistas puede tocar sus vistas.
vovahost

13
@vovahost es solo porque estás actualizando los componentes de la interfaz de usuario dentro del bloque del temporizador
Tim

10
Tenga en cuenta que java.util.Timer (y TimerTask) quedarán en desuso en JDK 9. TimerTask crea nuevos subprocesos para tareas que no son muy buenas.
Varvara Kalinina

183

Nota: Esta respuesta se dio cuando la pregunta no especificaba Android como contexto. Para obtener una respuesta específica al hilo de la interfaz de usuario de Android, consulte aquí.


Parece que la API de Mac OS permite que el hilo actual continúe y programa la tarea para que se ejecute de forma asincrónica. En Java, el java.util.concurrentpaquete proporciona la función equivalente . No estoy seguro de qué limitaciones podría imponer Android.

private static final ScheduledExecutorService worker = 
  Executors.newSingleThreadScheduledExecutor();

void someMethod() {
  
  Runnable task = new Runnable() {
    public void run() {
      /* Do something… */
    }
  };
  worker.schedule(task, 5, TimeUnit.SECONDS);
  
}

3
Esto nunca llama Runnable para mí
Supuhstar

14
Como nota al margen: esto también le permite cancelar la tarea más tarde, lo que podría ser útil en algunas situaciones. Simplemente almacene una referencia a la ScheduledFuture<?>devuelta por worker.schedule()y llame a su cancel(boolean)método.
Dennis

Creo que esta respuesta está desactualizada. .schedule ya no parece ser un método de Runnable ...? : /
beetree

55
@beetree es un método ScheduledExecutorService.
erickson

3
Esto no funciona si hay objetos de subproceso ui involucrados, debe llamar a runOnUIThread (new runnable () {run () ....}); o publicar un ejecutable usando el objeto controlador desde el interior de la ejecución () {}
Jayant Arora

107

Para ejecutar algo en el subproceso de la interfaz de usuario después de 5 segundos:

new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
    @Override
    public void run() {
        //Do something here
    }
}, 5000);

8
Confirme, esta es la mejor solución para evitar la llamada a looper.prepare y colocar todo en el hilo de la interfaz de usuario.
Tobliug

Gracias por esto, me ayudó con los problemas de Looper :)
Tia

1
Tendría cuidado al crear un controlador en el bucle principal, luego, en este hilo, no se debe hacer una tarea a largo plazo
Shayan_Aryan

40

puedes usar Handler dentro de UIThread:

runOnUiThread(new Runnable() {

    @Override
    public void run() {
         final Handler handler = new Handler();
         handler.postDelayed(new Runnable() {
           @Override
           public void run() {
               //add your code here
           }
         }, 1000);

    }
});

36

Gracias por todas las excelentes respuestas, encontré la solución que mejor se adapta a mis necesidades.

Handler myHandler = new DoSomething();
Message m = new Message();
m.obj = c;//passing a parameter here
myHandler.sendMessageDelayed(m, 1000);

class DoSomething extends Handler {
    @Override
    public void handleMessage(Message msg) {
      MyObject o = (MyObject) msg.obj;
      //do something here
    }
}

¿Está bien si uso este enfoque para recibir comentarios táctiles al hacer clic en un elemento ... view.setColor (some_color) y luego eliminar este color en el controlador después de x segundos ...?
eRaisedToX

25

KotlinY Javamuchas maneras

1. Usando Handler

Handler().postDelayed({
    TODO("Do something")
    }, 2000)

2. Usando TimerTask

Timer().schedule(object : TimerTask() {
    override fun run() {
        TODO("Do something")
    }
}, 2000)

O incluso más corto

Timer().schedule(timerTask {
    TODO("Do something")
}, 2000)

O más corto sería

Timer().schedule(2000) {
    TODO("Do something")
}

3. Usando Executors

Executors.newSingleThreadScheduledExecutor().schedule({
    TODO("Do something")
}, 2, TimeUnit.SECONDS)

En java

1. Usando Handler

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        //Do something
    }
}, 2000);

2. Usando Timer

new Timer().schedule(new TimerTask() {          
    @Override
    public void run() {
        // Do something
    }
}, 2000);

3. Usando ScheduledExecutorService

private static final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor();

Runnable runnable = new Runnable() {
  public void run() {
      // Do something
  }
  };
worker.schedule(runnable, 2, TimeUnit.SECONDS);

1
@ JanRabe Gracias por su sugerencia. Lo aprecio. Sin embargo la pregunta es How to call a method after a delay in Android. Así que me concentré en eso. Al punto. De lo contrario, Java Leaks es un gran tema para entender por separado para los desarrolladores.
Khemraj

20

Ver esta demostración:

import java.util.Timer;
import java.util.TimerTask;

class Test {
     public static void main( String [] args ) {
          int delay = 5000;// in ms 

          Timer timer = new Timer();

          timer.schedule( new TimerTask(){
             public void run() { 
                 System.out.println("Wait, what..:");
              }
           }, delay);

           System.out.println("Would it run?");
     }
}

20

Si tiene que usar el controlador, pero está en otro hilo, puede usarlo runonuithreadpara ejecutarlo en el hilo de la interfaz de usuario. Esto lo salvará de excepciones lanzadas pidiendo llamarLooper.Prepare()

runOnUiThread(new Runnable() {
    @Override
    public void run() {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                //Do something after 1 second
            }
        }, 1000);
    }
});

Parece bastante desordenado, pero esta es una de las formas.


44
Esto funciona, no puedo editar su publicación debido a las estúpidas reglas SO con un mínimo de 6 caracteres para editar, pero falta '()' después de 'nuevo controlador', debería ser 'nuevo controlador ()'
Jonathan Muller

2
En lugar de colocar todo en el hilo de la interfaz de usuario, puede hacer: nuevo controlador (Looper.getMainLooper ())
Tobliug

17

Prefiero usar el View.postDelayed()método, código simple a continuación:

mView.postDelayed(new Runnable() {
    @Override
    public void run() {
        // Do something after 1000 ms
    }
}, 1000);

1
¿No congela el elemento ui en sí, porque se programará en el controlador de vistas?
JacksOnF1re

1
No, la tarea publicada se ejecutará en 1 segundo, pero durante este segundo subproceso de interfaz de usuario realiza otro trabajo útil
demaksee

14

Aquí está mi solución más corta:

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        //Do something after 100ms
    }
}, 100);

10
final Handler handler = new Handler(); 
Timer t = new Timer(); 
t.schedule(new TimerTask() { 
    public void run() { 
        handler.post(new Runnable() { 
            public void run() { 
                //DO SOME ACTIONS HERE , THIS ACTIONS WILL WILL EXECUTE AFTER 5 SECONDS...
            }
        }); 
    } 
}, 5000); 

10

Si está utilizando Android Studio 3.0 y superior, puede usar expresiones lambda. El método callMyMethod()se llama después de 2 segundos:

new Handler().postDelayed(() -> callMyMethod(), 2000);

En caso de que necesite cancelar la ejecución demorada, use esto:

Handler handler = new Handler();
handler.postDelayed(() -> callMyMethod(), 2000);

// When you need to cancel all your posted runnables just use:
handler.removeCallbacksAndMessages(null);

¿Cómo podemos cancelar esto?
Damia Fuentes

Me sorprende la cantidad de personas que se mudarán felizmente a Kotlin, pero ignorarán por completo las expresiones Lambda que son Java estándar.
TomDK

6

Sugiero el temporizador , le permite programar un método para que se llame en un intervalo muy específico. Esto no bloqueará su interfaz de usuario y mantendrá su aplicación resonante mientras se ejecuta el método.

La otra opción, es wait (); método, esto bloqueará el hilo actual durante el período de tiempo especificado. Esto hará que su interfaz de usuario deje de responder si hace esto en el hilo de la interfaz de usuario.


2
Thread.sleep () es mejor que Object.wait (). Esperar implica que espera ser notificado y se está sincronizando en torno a alguna actividad. Dormir indica que simplemente no desea hacer nada durante un tiempo específico. El temporizador es el camino a seguir si desea que la acción suceda de forma asíncrona en algún momento posterior.
Tim Bender

1
Eso es verdad. Es por eso que lo enumeré como otra opción ;-)
Nate

6

Para un retraso de publicación de identificador de línea simple, puede hacer lo siguiente:

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        // Do someting
    }
}, 3000);

espero que esto ayude


5

Puede usar esto para la solución más simple:

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        //Write your code here
    }
}, 5000); //Timer is in ms here.

De lo contrario, a continuación puede haber otra solución limpia y útil:

new Handler().postDelayed(() -> 
{/*Do something here*/}, 
5000); //time in ms

5

Puede hacerlo mucho más limpio utilizando las expresiones lambda recién introducidas:

new Handler().postDelayed(() -> {/*your code here*/}, time);

5

Así que hay algunas cosas a tener en cuenta aquí, ya que hay muchas maneras de desollar a este gato. Aunque todas las respuestas ya han sido seleccionadas y elegidas. Creo que es importante que esto se revise con las pautas de codificación adecuadas para evitar que alguien vaya en la dirección incorrecta solo por la "respuesta simple seleccionada por la mayoría".

Primero, analicemos la respuesta simple Publicada diferida que es la respuesta seleccionada ganadora en general en este hilo.

Un par de cosas a tener en cuenta. Después de la demora posterior, puede encontrar fugas de memoria, objetos muertos, ciclos de vida que se han ido y más. Por lo tanto, manejarlo correctamente también es importante. Puedes hacer esto de dos maneras.

Por el bien del desarrollo moderno, suministraré en KOTLIN

Aquí hay un ejemplo simple de usar el hilo de la interfaz de usuario en una devolución de llamada y confirmar que su actividad todavía está viva y bien cuando golpea su devolución de llamada.

  Handler(Looper.getMainLooper()).postDelayed({
            if(activity != null && activity?.isFinishing == false){
                txtNewInfo.visibility = View.GONE
            }
        }, NEW_INFO_SHOW_TIMEOUT_MS)

Sin embargo, esto aún no es perfecto, ya que no hay razón para cancelar su devolución de llamada si la actividad ha desaparecido. así que una mejor manera sería mantener una referencia y eliminar sus devoluciones de llamada como esta.

    private fun showFacebookStylePlus1NewsFeedOnPushReceived(){
        A35Log.v(TAG, "showFacebookStylePlus1NewsFeedOnPushReceived")
        if(activity != null && activity?.isFinishing == false){
            txtNewInfo.visibility = View.VISIBLE
            mHandler.postDelayed({
                if(activity != null && activity?.isFinishing == false){
                    txtNewInfo.visibility = View.GONE
                }
            }, NEW_INFO_SHOW_TIMEOUT_MS)
        }
    }

y, por supuesto, manejar la limpieza en onPause para que no toque la devolución de llamada.

    override fun onPause() {
        super.onPause()
        mHandler.removeCallbacks(null)
    }

Ahora que hemos hablado a través de lo obvio, hablemos de una opción más limpia con las rutinas modernas y kotlin :). Si aún no los está utilizando, realmente se los está perdiendo.

   fun doActionAfterDelay() 
        launch(UI) {
            delay(MS_TO_DELAY)           
            actionToTake()
        }
    }

o si desea hacer siempre un inicio de UI en ese método, simplemente puede hacer:

  fun doActionAfterDelay() = launch(UI){ 
      delay(MS_TO_DELAY)           
      actionToTake()
  }

Por supuesto, al igual que PostDelayed, debe asegurarse de manejar la cancelación para que pueda hacer las verificaciones de actividad después de la llamada de retraso o puede cancelarla en onPause al igual que la otra ruta.

var mDelayedJob: Job? = null
fun doActionAfterDelay() 
   mDelayedJob = launch(UI) {
            try {
               delay(MS_TO_DELAY)           
               actionToTake()
            }catch(ex: JobCancellationException){
                showFancyToast("Delayed Job canceled", true, FancyToast.ERROR, "Delayed Job canceled: ${ex.message}")
            }
        }
   }
}

// manejar la limpieza

override fun onPause() {
   super.onPause()
   if(mDelayedJob != null && mDelayedJob!!.isActive) {
      A35Log.v(mClassTag, "canceling delayed job")
      mDelayedJob?.cancel() //this should throw CancelationException in coroutine, you can catch and handle appropriately
   }
}

Si coloca el inicio (UI) en la firma del método, el trabajo puede asignarse en la línea de código de llamada.

así que la moraleja de la historia es estar seguro con sus acciones retrasadas, asegurarse de eliminar sus devoluciones de llamada o cancelar sus trabajos y, por supuesto, confirmar que tiene el ciclo de vida correcto para tocar elementos en su devolución de llamada demorada completa. The Coroutines también ofrece acciones cancelables.

También vale la pena señalar que normalmente debe manejar las diversas excepciones que pueden venir con las corutinas. Por ejemplo, una cancelación, una excepción, un tiempo de espera, lo que decida usar. Aquí hay un ejemplo más avanzado si realmente decide comenzar a utilizar corutinas.

   mLoadJob = launch(UI){
            try {
                //Applies timeout
                withTimeout(4000) {
                    //Moves to background thread
                    withContext(DefaultDispatcher) {
                        mDeviceModelList.addArrayList(SSDBHelper.getAllDevices())
                    }
                }

                //Continues after async with context above
                showFancyToast("Loading complete", true, FancyToast.SUCCESS)
            }catch(ex: JobCancellationException){
                showFancyToast("Save canceled", true, FancyToast.ERROR, "Save canceled: ${ex.message}")
            }catch (ex: TimeoutCancellationException) {
                showFancyToast("Timed out saving, please try again or press back", true, FancyToast.ERROR, "Timed out saving to database: ${ex.message}")
            }catch(ex: Exception){
                showFancyToast("Error saving to database, please try again or press back", true, FancyToast.ERROR, "Error saving to database: ${ex.message}")
            }
        }

1
No hay problema, Rajiv, iría un paso más allá y mencionaría que usando Live Data las corutinas pueden ser conscientes del ciclo de vida y auto cancelables para evitar las llamadas de limpieza, pero no quiero arrojar demasiadas curvas de aprendizaje en una sola respuesta;)
Sam

3

Creé un método más simple para llamar a esto.

public static void CallWithDelay(long miliseconds, final Activity activity, final String methodName)
    {
        new Handler().postDelayed(new Runnable() {

            @Override
            public void run() {
                try {
                    Method method =  activity.getClass().getMethod(methodName);
                    method.invoke(activity);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }, miliseconds);
    }

Para usarlo, solo llame al: .CallWithDelay(5000, this, "DoSomething");


3
¿Reflexión para una tarea tan básica?
Max Ch

Desde la pregunta al método de llamada similar a iOS performSelector. Esta es la mejor manera de hacerlo.
HelmiB

3

Debajo de uno funciona cuando llegas,

java.lang.RuntimeException: no se puede crear un controlador dentro del hilo que no haya llamado Looper.prepare ()

final Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
  @Override
  public void run() {
    //Do something after 100ms
  }
}, 100);

3

Usando Kotlin, podemos lograr haciendo lo siguiente

Handler().postDelayed({
    // do something after 1000ms 
}, 1000)


2

Si usa RxAndroid, el manejo de hilos y errores se vuelve mucho más fácil. El siguiente código se ejecuta después de un retraso

   Observable.timer(delay, TimeUnit.SECONDS)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(aLong -> {
           // Execute code here
        }, Throwable::printStackTrace);

1

todos parecen olvidar limpiar el controlador antes de publicar un nuevo ejecutable o mensaje en él. De lo contrario, podrían acumularse y causar un mal comportamiento.

handler.removeMessages(int what);
// Remove any pending posts of messages with code 'what' that are in the message queue.

handler.removeCallbacks(Runnable r)
// Remove any pending posts of Runnable r that are in the message queue.

1

Aquí hay otra forma complicada: no arrojará excepciones cuando los elementos de la IU de cambio ejecutable.

public class SimpleDelayAnimation extends Animation implements Animation.AnimationListener {

    Runnable callBack;

    public SimpleDelayAnimation(Runnable runnable, int delayTimeMilli) {
        setDuration(delayTimeMilli);
        callBack = runnable;
        setAnimationListener(this);
    }

    @Override
    public void onAnimationStart(Animation animation) {

    }

    @Override
    public void onAnimationEnd(Animation animation) {
        callBack.run();
    }

    @Override
    public void onAnimationRepeat(Animation animation) {

    }
}

Puedes llamar a la animación así:

view.startAnimation(new SimpleDelayAnimation(delayRunnable, 500));

La animación se puede adjuntar a cualquier vista.



1

Me gustan las cosas más limpias: aquí está mi implementación, código en línea para usar dentro de su método

new Handler().postDelayed(new Runnable() {
  @Override
  public void run() {
    //Do something after 100ms
  }
}, 100);

0

Una solución adecuada en Android:

private static long SLEEP_TIME = 2 // for 2 second
.
.
MyLauncher launcher = new MyLauncher();
            launcher.start();
.
.
private class MyLauncher extends Thread {
        @Override
        /**
         * Sleep for 2 seconds as you can also change SLEEP_TIME 2 to any. 
         */
        public void run() {
            try {
                // Sleeping
                Thread.sleep(SLEEP_TIME * 1000);
            } catch (Exception e) {
                Log.e(TAG, e.getMessage());
            }
            //do something you want to do
           //And your code will be executed after 2 second
        }
    }

0

Solución similar pero mucho más limpia para usar

Escribe esta función fuera de la clase

fun delay(duration: Long, `do`: () -> Unit) {

    Handler().postDelayed(`do`, duration)

}

Uso:

delay(5000) {
    //Do your work here
}

¿Qué hace el "hacer"?
Skizo-ozᴉʞS

solo un nombre, mantén cualquier cosa allí. does un método incorporado, así que tenemos que usar `para usarlo como nombre de variable
Manohar Reddy

Gracias, pero ¿por qué se usa este nombre de variable? Me refiero a cuál es su función.
Skizo-ozᴉʞS

1
Pensé así dodespués de 3 segundos de retraso
Manohar Reddy

0

En Android, podemos escribir a continuación el código kotlin para retrasar la ejecución de cualquier función

class MainActivity : AppCompatActivity() {

private lateinit var handler: Handler

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    handler= Handler()
    handler.postDelayed({
        doSomething()
    },2000)
}

private fun doSomething() {
    Toast.makeText(this,"Hi! I am Toast Message",Toast.LENGTH_SHORT).show()
}
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.