¿Repetir una tarea con un retraso de tiempo?

11 minutos de lectura

¿Repetir una tarea con un retraso de tiempo
Kalpa Pathum Welivitigoda

Tengo una variable en mi código que dice que es “estado”.

Quiero mostrar algo de texto en la aplicación dependiendo de este valor variable. Esto tiene que hacerse con un retraso de tiempo específico.

Es como,

  • Comprobar el valor de la variable de estado

  • Mostrar algo de texto

  • Espere 10 segundos

  • Comprobar el valor de la variable de estado

  • Mostrar algo de texto

  • Espere 15 segundos

y así. El tiempo de retardo puede variar y se establece una vez que se muestra el texto.

Yo he tratado Thread.sleep(time delay) y fracasó. ¿Alguna forma mejor de hacer esto?

  • Pregunta relacionada: repetir una tarea con un retraso de tiempo dentro de una vista personalizada

    – Suragch

    12 de junio de 2017 a las 11:10

¿Repetir una tarea con un retraso de tiempo
inazaruk

Deberías usar Handler‘s postDelayed función para este fin. Ejecutará su código con un retraso especificado en el subproceso principal de la interfaz de usuario, por lo que podrá actualizar los controles de la interfaz de usuario.

private int mInterval = 5000; // 5 seconds by default, can be changed later
private Handler mHandler;

@Override
protected void onCreate(Bundle bundle) {

    // your code here

    mHandler = new Handler();
    startRepeatingTask();
}

@Override
public void onDestroy() {
    super.onDestroy();
    stopRepeatingTask();
}

Runnable mStatusChecker = new Runnable() {
    @Override 
    public void run() {
          try {
               updateStatus(); //this function can change value of mInterval.
          } finally {
               // 100% guarantee that this always happens, even if
               // your update method throws an exception
               mHandler.postDelayed(mStatusChecker, mInterval);
          }
    }
};

void startRepeatingTask() {
    mStatusChecker.run(); 
}

void stopRepeatingTask() {
    mHandler.removeCallbacks(mStatusChecker);
}

  • Gracias inazaruk, logré hacerlo funcionar. Encontré 2 v pequeños errores tipográficos (en la parte superior es “Handler” no “Handle” y en la parte inferior es “removeCallbacks” no elimina “removecallback”. Pero en cualquier caso, el código era exactamente lo que estaba buscando. Tratando de pensar lo que puedo hacer para devolver el favor. Al menos te has ganado mi respeto. Saludos cordiales Aubrey Bourke.

    – aubreybourke

    16 de diciembre de 2011 a las 12:13

  • Buen programa, funciona absolutamente bien. Pero se tuvo que llamar a startRepeatingTask() desde el subproceso onCreate method /UI (¡me tomó un tiempo darme cuenta de esto!), tal vez este punto podría haberse mencionado en alguna parte. Saludos

    – gkris

    21 de agosto de 2012 a las 21:28

  • ¿Hay alguna forma de tener un Runnable repetido dentro del método getView() de un adaptador?

    – Étienne Lawlor

    22 de julio de 2014 a las 6:53

  • Aquí, cuando importamos clases, ¿qué debemos importar? android.os.Handler o java.util.logging.Handler?

    – EJ Chathuranga

    26 de septiembre de 2017 a las 9:29


  • @inazruk, tu solución sí no funciona si tiene que ejecutar stopRepeatingTask() desde dentro del ejecutable, por ejemplo, después de verificar el valor de la variable. Dado que está retrasado dentro de un bloque finalmente.

    – como judo

    8 de enero de 2018 a las 16:41


¿Repetir una tarea con un retraso de tiempo
ravemir

Para cualquier persona interesada, aquí hay una clase que creé usando el código de inazaruk que crea todo lo necesario (la llamé UIUpdater porque la uso para actualizar periódicamente la interfaz de usuario, pero puede llamarla como quiera):

import android.os.Handler;
/**
 * A class used to perform periodical updates,
 * specified inside a runnable object. An update interval
 * may be specified (otherwise, the class will perform the 
 * update every 2 seconds).
 * 
 * @author Carlos Simões
 */
public class UIUpdater {
        // Create a Handler that uses the Main Looper to run in
        private Handler mHandler = new Handler(Looper.getMainLooper());

        private Runnable mStatusChecker;
        private int UPDATE_INTERVAL = 2000;

        /**
         * Creates an UIUpdater object, that can be used to
         * perform UIUpdates on a specified time interval.
         * 
         * @param uiUpdater A runnable containing the update routine.
         */
        public UIUpdater(final Runnable uiUpdater) {
            mStatusChecker = new Runnable() {
                @Override
                public void run() {
                    // Run the passed runnable
                    uiUpdater.run();
                    // Re-run it after the update interval
                    mHandler.postDelayed(this, UPDATE_INTERVAL);
                }
            };
        }

        /**
         * The same as the default constructor, but specifying the
         * intended update interval.
         * 
         * @param uiUpdater A runnable containing the update routine.
         * @param interval  The interval over which the routine
         *                  should run (milliseconds).
         */
        public UIUpdater(Runnable uiUpdater, int interval){
            UPDATE_INTERVAL = interval;
            this(uiUpdater);
        }

        /**
         * Starts the periodical update routine (mStatusChecker 
         * adds the callback to the handler).
         */
        public synchronized void startUpdates(){
            mStatusChecker.run();
        }

        /**
         * Stops the periodical update routine from running,
         * by removing the callback.
         */
        public synchronized void stopUpdates(){
            mHandler.removeCallbacks(mStatusChecker);
        }
}

Luego puede crear un objeto UIUpdater dentro de su clase y usarlo así:

...
mUIUpdater = new UIUpdater(new Runnable() {
         @Override 
         public void run() {
            // do stuff ...
         }
    });

// Start updates
mUIUpdater.startUpdates();

// Stop updates
mUIUpdater.stopUpdates();
...

Si desea usar esto como un actualizador de actividad, coloque la llamada de inicio dentro del método onResume() y la llamada de detención dentro de onPause(), para que las actualizaciones comiencen y se detengan de acuerdo con la visibilidad de la actividad.

  • Editado: UPDATE_INTERVAL = interval; debiera ser antes de this(uiUpdater); en UIUpdater(Runnable uiUpdater, int interval) (como el valor de UPDATE_INTERVAL se utiliza y debe ser el que se pasa como parámetro interval;). También evite el ancho de más de 80 caracteres en el código cuando sea posible (casi siempre;)

    – Sr_y_Sra_D

    15 de marzo de 2013 a las 9:24


  • Esta clase tiene una serie de problemas. En primer lugar, debe instanciarse en el hilo principal para poder actualizar la GUI. Podrías haber resuelto esto pasando el looper principal al constructor del controlador: new Handler(Looper.getMainLooper()). En segundo lugar, no valida los argumentos, por lo que se traga Runnables nulos e intervalos negativos. Finalmente, no tiene en cuenta el tiempo pasado en el uiUpdater.run() línea, ni maneja las posibles excepciones lanzadas por ese método. Además, no es seguro para subprocesos, debe hacer start y stop métodos sincronizados.

    – Señor Smith

    22 de agosto de 2013 a las 7:39

  • Editado hasta la parte de validación de argumentos, ya que no tengo Eclipse aquí para probar el código. ¡Gracias por la respuesta! ¿Es esto lo que quisiste decir? StartUpdates y stopUpdates sincronizados y coloque una llamada Looper.getMainLooper() dentro del controlador del controlador (espero que pueda llamarlo directamente desde la declaración del campo)

    – ravemir

    22 de agosto de 2013 a las 15:12

  • Entiendo esto: error: call to this must be first statement in constructor tal vez hay una solución fácil.

    – msysmilu

    23 de abril de 2015 a las 14:33

  • Votar a favor por tener importación: lleva tiempo averiguar de dónde proviene el controlador cuando se programa en Java de forma casual

    – Román Susi

    28/10/2015 a las 18:37

Creo que el nuevo picor es usar un ScheduledThreadPoolExecutor. Al igual que:

private final ScheduledThreadPoolExecutor executor_ = 
        new ScheduledThreadPoolExecutor(1);
this.executor_.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
    update();
    }
}, 0L, kPeriod, kTimeUnit);

  • Executors.newSingleThreadScheduledExecutor() puede ser otra opción aquí.

    – Gulshan

    29/10/2015 a las 11:59

1646976012 296 ¿Repetir una tarea con un retraso de tiempo
hitesh sahu

Hay 3 formas de hacerlo:

Usar ScheduledThreadPoolExecutor

Un poco exagerado ya que no necesitas un grupo de Thread

   //----------------------SCHEDULER-------------------------
    private final ScheduledThreadPoolExecutor executor_ =
            new ScheduledThreadPoolExecutor(1);
     ScheduledFuture<?> schedulerFuture;
   public void  startScheduler() {
       schedulerFuture=  executor_.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                //DO YOUR THINGS
                pageIndexSwitcher.setVisibility(View.GONE);
            }
        }, 0L, 5*MILLI_SEC,  TimeUnit.MILLISECONDS);
    }


    public void  stopScheduler() {
        pageIndexSwitcher.setVisibility(View.VISIBLE);
        schedulerFuture.cancel(false);
        startScheduler();
    }

Usar tarea de temporizador

Estilo antiguo de Android

    //----------------------TIMER  TASK-------------------------

    private Timer carousalTimer;
    private void startTimer() {
        carousalTimer = new Timer(); // At this line a new Thread will be created
        carousalTimer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                //DO YOUR THINGS
                pageIndexSwitcher.setVisibility(INVISIBLE);
            }
        }, 0, 5 * MILLI_SEC); // delay
    }

    void stopTimer() {
        carousalTimer.cancel();
    }

Usar controlador y ejecutable

Estilo moderno de Android

    //----------------------HANDLER-------------------------

    private Handler taskHandler = new android.os.Handler();

    private Runnable repeatativeTaskRunnable = new Runnable() {
        public void run() {
            //DO YOUR THINGS
        }
    };

   void startHandler() {
        taskHandler.postDelayed(repeatativeTaskRunnable, 5 * MILLI_SEC);
    }

    void stopHandler() {
        taskHandler.removeCallbacks(repeatativeTaskRunnable);
    }

Controlador sin fugas con actividad/contexto

Declare una clase de controlador interno que no pierda memoria en su clase de actividad/fragmento

/**
     * Instances of static inner classes do not hold an implicit
     * reference to their outer class.
     */
    private static class NonLeakyHandler extends Handler {
        private final WeakReference<FlashActivity> mActivity;

        public NonLeakyHandler(FlashActivity activity) {
            mActivity = new WeakReference<FlashActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            FlashActivity activity = mActivity.get();
            if (activity != null) {
                // ...
            }
        }
    }

Declare un ejecutable que realizará su tarea repetitiva en su clase Actividad/Fragmento

   private Runnable repeatativeTaskRunnable = new Runnable() {
        public void run() {
            new Handler(getMainLooper()).post(new Runnable() {
                @Override
                public void run() {

         //DO YOUR THINGS
        }
    };

Inicialice el objeto Handler en su Actividad/Fragmento (aquí FlashActivity es mi clase de actividad)

//Task Handler
private Handler taskHandler = new NonLeakyHandler(FlashActivity.this);

Para repetir una tarea después de un intervalo de tiempo fijo

taskHandler.postDelayed(repeatativeTaskRunnable, DELAY_MILLIS);

Para detener la repetición de la tarea.

taskHandler .removeCallbacks(repeatativeTaskRunnable );

ACTUALIZACIÓN: En Kotlin:

    //update interval for widget
    override val UPDATE_INTERVAL = 1000L

    //Handler to repeat update
    private val updateWidgetHandler = Handler()

    //runnable to update widget
    private var updateWidgetRunnable: Runnable = Runnable {
        run {
            //Update UI
            updateWidget()
            // Re-run it after the update interval
            updateWidgetHandler.postDelayed(updateWidgetRunnable, UPDATE_INTERVAL)
        }

    }

 // SATART updating in foreground
 override fun onResume() {
        super.onResume()
        updateWidgetHandler.postDelayed(updateWidgetRunnable, UPDATE_INTERVAL)
    }


    // REMOVE callback if app in background
    override fun onPause() {
        super.onPause()
        updateWidgetHandler.removeCallbacks(updateWidgetRunnable);
    }

1641946699 76 ¿Debo evitar estrictamente el uso de enumeraciones en Android
kai wang

El temporizador funciona bien. Aquí, uso el temporizador para buscar texto después de 1,5 s y actualizar la interfaz de usuario. Espero que ayude.

private Timer _timer = new Timer();

_timer.schedule(new TimerTask() {
    @Override
    public void run() {
        // use runOnUiThread(Runnable action)
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                search();
            }
        });
    }
}, timeInterval);

  • ¿Dónde pusiste el intervalo de tiempo?

    – Nathiel Barros

    5 de julio de 2017 a las 8:56

  • Hola Nathiel, acabo de actualizar mi publicación, ¡espero que te ayude! El tiempo de intervalo es el segundo parámetro de Timer.schedule().

    –Kai Wang

    5 de julio de 2017 a las 18:03

Usar kotlin y su Coroutine es bastante fácil, primero declara un trabajo en tu clase (mejor en tu modelo de vista) así:

private var repeatableJob: Job? = null

luego, cuando quieras crearlo e iniciarlo, haz esto:

repeatableJob = viewModelScope.launch {
    while (isActive) {
         delay(5_000)
         loadAlbums(iImageAPI, titleHeader, true)
    }
}
repeatableJob?.start()

y si quieres terminarlo:

repeatableJob?.cancel()

PD: viewModelScope solo está disponible en modelos de vista, puede usar otros ámbitos Coroutine como withContext(Dispatchers.IO)

Más información: Aquí

  • ¿Dónde pusiste el intervalo de tiempo?

    – Nathiel Barros

    5 de julio de 2017 a las 8:56

  • Hola Nathiel, acabo de actualizar mi publicación, ¡espero que te ayude! El tiempo de intervalo es el segundo parámetro de Timer.schedule().

    –Kai Wang

    5 de julio de 2017 a las 18:03

El temporizador es otra forma de hacer su trabajo, pero no se preocupe, asegúrese de agregar runOnUiThread si está trabajando con la interfaz de usuario.

    import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;

import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.TextView;
import android.app.Activity;

public class MainActivity extends Activity {

 CheckBox optSingleShot;
 Button btnStart, btnCancel;
 TextView textCounter;

 Timer timer;
 MyTimerTask myTimerTask;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  optSingleShot = (CheckBox)findViewById(R.id.singleshot);
  btnStart = (Button)findViewById(R.id.start);
  btnCancel = (Button)findViewById(R.id.cancel);
  textCounter = (TextView)findViewById(R.id.counter);

  btnStart.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View arg0) {

    if(timer != null){
     timer.cancel();
    }

    //re-schedule timer here
    //otherwise, IllegalStateException of
    //"TimerTask is scheduled already" 
    //will be thrown
    timer = new Timer();
    myTimerTask = new MyTimerTask();

    if(optSingleShot.isChecked()){
     //singleshot delay 1000 ms
     timer.schedule(myTimerTask, 1000);
    }else{
     //delay 1000ms, repeat in 5000ms
     timer.schedule(myTimerTask, 1000, 5000);
    }
   }});

  btnCancel.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    if (timer!=null){
     timer.cancel();
     timer = null;
    }
   }
  });

 }

 class MyTimerTask extends TimerTask {

  @Override
  public void run() {
   Calendar calendar = Calendar.getInstance();
   SimpleDateFormat simpleDateFormat = 
     new SimpleDateFormat("dd:MMMM:yyyy HH:mm:ss a");
   final String strDate = simpleDateFormat.format(calendar.getTime());

   runOnUiThread(new Runnable(){

    @Override
    public void run() {
     textCounter.setText(strDate);
    }});
  }

 }

}

y xml es…

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context=".MainActivity" >

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:autoLink="web"
    android:text="http://android-er.blogspot.com/"
    android:textStyle="bold" />
<CheckBox 
    android:id="@+id/singleshot"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Single Shot"/>

Otra forma de usar CountDownTimer

new CountDownTimer(30000, 1000) {

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

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

Programe una cuenta regresiva hasta un momento en el futuro, con notificaciones periódicas en intervalos a lo largo del camino. Ejemplo de mostrar una cuenta regresiva de 30 segundos en un campo de texto:

Para detalles

  • Se prefiere el controlador sobre el temporizador. Ver Temporizador vs Controlador

    – Suragch

    12 de junio de 2017 a las 4:39

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad