¿Pausar / detener e iniciar / reanudar Java TimerTask continuamente?

Tengo una pregunta simple sobre Java TimerTask. ¿Cómo pausar / reanudar dos tareas TimerTask en función de una determinada condición? Por ejemplo, tengo dos temporizadores que se ejecutan entre ellos. Cuando se ha cumplido una determinada condición dentro de la tarea del primer temporizador, el primer temporizador se detiene e inicia el segundo temporizador, y lo mismo sucede cuando se cumple una determinada condición dentro de la tarea del segundo temporizador. La clase a continuación muestra exactamente lo que quiero decir:

public class TimerTest { Timer timer1; Timer timer2; volatile boolean a = false; public TimerTest() { timer1 = new Timer(); timer2 = new Timer(); } public void runStart() { timer1.scheduleAtFixedRate(new Task1(), 0, 1000); } class Task1 extends TimerTask { public void run() { System.out.println("Checking a"); a = SomeClass.getSomeStaticValue(); if (a) { // Pause/stop timer1, start/resume timer2 for 5 seconds timer2.schedule(new Task2(), 5000); } } } class Task2 extends TimerTask{ public void run() { System.out.println("Checking a"); a = SomeClass.getSomeStaticValue(); if (!a) { // Pause/stop timer2, back to timer1 timer1.scheduleAtFixedRate(new Task1(), 0, 1000); } // Do something... } } public static void main(String args[]) { TimerTest tt = new TimerTest(); tt.runStart(); } } 

Así que mi pregunta es, ¿cómo pause timer1 mientras timer2 y viceversa mientras timer2 está ejecutando? El rendimiento y el tiempo es mi principal preocupación ya que esto debe implementarse dentro de otro hilo en funcionamiento. Por cierto, estoy tratando de implementar estos temporizadores simultáneos en Android.

¡Gracias por tu ayuda!

Desde TimerTask.cancel() :

Tenga en cuenta que llamar a este método desde el método de ejecución de una tarea de temporizador repetitivo garantiza absolutamente que la tarea del temporizador no se ejecutará nuevamente.

Entonces, una vez cancelado, nunca volverá a funcionar. En su lugar, sería mejor utilizar el ScheduledExecutorService más moderno (de Java 5+).

Editar: La construcción básica es:

 ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor(); exec.scheduleAtFixedRate(runnable, 0, 1000, TimeUnit.MILLISECONDS); 

pero al analizarlo no hay forma de cancelar esa tarea una vez que se inició sin cerrar el servicio, lo cual es un poco extraño.

TimerTask puede ser más fácil en este caso, pero necesitará crear una nueva instancia cuando inicie una. No puede ser reutilizado.

Alternativamente, puede encapsular cada tarea como un servicio transitorio separado:

 final ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor(); Runnable task1 = new Runnable() { public void run() { a++; if (a == 3) { exec.shutdown(); exec = Executors.newSingleThreadScheduledExecutor(); exec.scheduleAtFixedRate(task2, 0, 1000, TimeUnit.MILLISECONDS) } } }; exec.scheduleAtFixedRate(task1, 0, 1000, TimeUnit.MILLISECONDS); 

La solución más fácil que encontré: simplemente agregue un valor booleano en el código de ejecución en la tarea del temporizador, así:

 timer.schedule( new TimerTask() { public void run() { if(!paused){ //do your thing } } }, 0, 1000 ); 

Si ya ha cancelado un temporizador, no puede reiniciarlo, tendrá que crear uno nuevo.

Vea esta respuesta , contiene un video y el código fuente de cómo hice algo similar.

Básicamente hay dos métodos: pause y resume

En pausa:

 public void pause() { this.timer.cancel(); } 

En resumen:

 public void resume() { this.timer = new Timer(); this.timer.schedule( aTask, 0, 1000 ); } 

Eso hace que la percepción de pausa / reanudar.

Si sus temporizadores realizan acciones diferentes según el estado de la aplicación, puede considerar usar el StatePattern

Puño define un estado abstracto:

 abstract class TaskState { public void run(); public TaskState next(); } 

Y proporcione tantos estados como desee. La clave es que un estado te lleva a otro.

 class InitialState extends TaskState { public void run() { System.out.println( "starting..."); } public TaskState next() { return new FinalState(); } } class FinalState extends TaskState { public void run() { System.out.println("Finishing..."); } public TaskState next(){ return new InitialState(); } } 

Y luego cambias el estado en tu temporizador.

 Timer timer = new Timer(); TaskState state = new InitialState(); timer.schedule( new TimerTask() { public void run() { this.state.run(); if( shouldChangeState() ) { this.state = this.state.next(); } } }, 0, 1000 ); 

Finalmente, si lo que necesita es realizar lo mismo, pero a diferentes velocidades, puede considerar usar TimingFramework . Es un poco más complejo, pero te permite hacer animaciones geniales, permitiendo que la pintura de cierto componente tenga lugar a diferentes ritmos (en lugar de ser lineal)

Revisando su código fuente, aquí están los cambios (que prácticamente validan mi respuesta anterior)

En task1:

 // Stop timer1 and start timer2 timer1.cancel(); timer2 = new Timer(); // <-- just insert this line timer2.scheduleAtFixedRate(new Task2(), 0, 1000); 

y en task2:

 // Stop timer2 and start timer1 timer2.cancel(); timer1 = new Timer(); // <-- just insert this other timer1.scheduleAtFixedRate(new Task1(), 0, 1000); 

Se ejecuta en mi máquina:

¡funciona! http://img687.imageshack.us/img687/9954/capturadepantalla201001m.png

En mi opinión, esto es un poco equivocado. Si su código necesita garantías de tiempo, no puede usar el temporizador de todos modos, ni lo desea. “Esta clase no ofrece garantías en tiempo real: progtwig tareas usando el método Object.wait (long)”.

La respuesta, en mi humilde opinión, es que no desea pausar y reiniciar sus temporizadores. Simplemente quiere suprimir sus métodos de ejecución de hacer sus negocios. Y eso es fácil: simplemente envuélvelos en una statement if . El interruptor está encendido, se ejecutan, el interruptor está apagado, pierden ese ciclo.

Editar: La pregunta ha cambiado sustancialmente de lo que era originalmente, pero dejaré esta respuesta en caso de que ayude a alguien. Mi punto es: si no te importa cuando tu evento se dispara en el lapso de milisegundos N (solo que no EXCEDE una vez cada N milisegundos), puedes usar condicionales en los métodos de ejecución. Esto es, de hecho, un caso muy común, especialmente cuando N es menos de 1 segundo.

Android no volverá a utilizar un TimerTask que ya se haya progtwigdo una vez. Por lo tanto, es necesario reinstalar tanto el Timer como el TimerTask, por ejemplo, así en un Fragment:

 private Timer timer; private TimerTask timerTask; public void onResume () { super.onResume(); timer = new Timer(); timerTask = new MyTimerTask(); timer.schedule(timerTask, 0, 1000); } public void onPause () { super.onPause(); timer.cancel(); // Renders Timer unusable for further schedule() calls. } 

Puedo detener un temporizador y una tarea usando el siguiente código:

  if(null != timer) { timer.cancel(); Log.i(LOG_TAG,"Number of cancelled tasks purged: " + timer.purge()); timer = null; } if(task != null) { Log.i(LOG_TAG,"Tracking cancellation status: " + task.cancel()); task = null; }