2010-01-20 43 views
18

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 de abajo muestra exactamente lo que quiero decir:Pausar/detener e iniciar/reanudar Java TimerTask continuamente?

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 se detienen timer1 mientras se ejecuta timer2 y viceversa, mientras que timer2 se 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 su ayuda!

+0

¿Cuál es la excepción? – cletus

+2

Excepción en el hilo "Timer-1" java.lang.IllegalStateException: El temporizador ya ha sido cancelado. – Faiz

+0

Es posible que desee pensar en pasar a 'java.util.concurrent'. 'Timer' se considera un poco de la vieja escuela. –

Respuesta

24

De TimerTask.cancel():

Tenga en cuenta que llamar a este método de dentro del método de ejecución de una tarea temporizador de repetición garantiza absolutamente que la tarea de temporizador no se ejecutará de nuevo.

Una vez cancelado, no se ejecutará nunca más. Sería mejor utilizar el más moderno ScheduledExecutorService (de Java 5+).

Editar: La construcción básica es:

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

pero mirando en ella no hay manera de cancelar esa tarea una vez que su comenzado sin necesidad de apagar el servicio, que es un poco extraño.

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

alternativa usted puede encapsular cada tarea como un servicio separado transitoria:

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); 
+1

¿Podría mostrarme cómo implementar el 'ScheduledExceutorService' en la clase anterior? gracias – Faiz

+0

No es la mejor idea para cerrar el servicio del ejecutor, puede cancelar una tarea a través de ScheduledFuture devuelto por scheduleAtFixedRate() – axtavt

+0

@axtavt: 'cancelar()' detendrá una tarea que ya se está ejecutando – cletus

6

Si ya ha cancelado un temporizador, no se puede volver a empezar, usted tiene que crear una nueva.

Vea este answer, 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/reanudación.

Si sus temporizadores realizan diferentes acciones en función del estado de la aplicación que puede considerar usar el StatePattern

Puño definir un estado abstracto:

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

y proporcionar la mayor cantidad de estados que lo 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 cambia el estado en su 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); 

Por último, si lo que necesita es llevar a cabo la misma cosa, pero a un ritmo diferente, es posible considerar el uso de la TimingFramework. Es un poco más complejo, pero permite hacer animaciones geniales, permitiendo que la pintura de ciertos componentes tenga lugar a diferentes velocidades (en lugar de ser lineal)

+1

https://timingframework.dev.java.net/ Enlace no funcionado, reconsidere –

4

Revisando su código fuente, aquí están los cambios (que más o menos 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:

it works! http://img687.imageshack.us/img687/9954/capturadepantalla201001m.png

+3

buen fondo en la ventana de la terminal :) –

+0

Bien, pero ¿y si quiero cambiar el período de dos temporizadores? Digamos que el período del temporizador 1 es 5000 y el temporizador 2 es 1000, entonces supongo que esto arruinará todo el código. En ese caso, ¿cómo podría hacerlo? –

4

En mi opinión, esto es un poco equivocada. 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: programa 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élvalos en una declaración 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 voy a 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.

+0

En la mayoría de los casos, esta es una solución muy confiable y simple. +1 – Alex

13

solución más fácil que he encontrado: sólo tiene que añadir un valor lógico en el código de ejecución en la tarea de temporizador, así:

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

Creo que esto no es eficiente. El temporizador seguirá funcionando y verificando el valor booleano cada vez. – Alex

+0

Dudo que la eficiencia sea una preocupación, ya que en la práctica es probable que haya más operaciones que requieren mucho tiempo en la tarea que un solo condicional. Lo que me preocupa de este enfoque es que la tarea puede dispararse en cualquier momento después de haber sido "reanudada". Esto podría ser notable por largos intervalos de tiempo. – Jakob

0

soy capaz de detener un temporizador y una tarea utilizando código siguiente:

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

Android no reutilizará un TimerTask que ya se ha programado una vez. Por lo tanto, es necesario reinstalar tanto el Timer como el TimerTask, por ejemplo, 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. 
} 
Cuestiones relacionadas