J'ai une simple question concernant Java TimerTask. Comment suspendre/reprendre deux tâches TimerTask en fonction d'une certaine condition? Par exemple, j'ai deux minuteries qui fonctionnent entre elles. Lorsqu'une certaine condition a été remplie dans la tâche du premier minuteur, le premier minuteur arrête et démarre le second minuteur, et la même chose se produit lorsqu'une certaine condition a été remplie dans la tâche du second minuteur. La classe ci-dessous montre exactement ce que je veux dire:
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();
}
}
Ma question est donc la suivante: comment mettre en pause timer1
pendant l'exécution de timer2
et vice-versa pendant que timer2
est en cours d'exécution? La performance et le timing sont mes principales préoccupations car cela doit être implémenté dans un autre thread en cours d'exécution. En passant, j'essaie d'implémenter ces timers simultanés sur Android.
Merci de votre aide!
De TimerTask.cancel()
:
Notez que l'appel de cette méthode depuis dans la méthode d'exécution d'une répétition La minuterie garantit absolument que la tâche du minuteur ne sera pas exécutée à nouveau.
Donc, une fois annulé, il ne fonctionnera plus jamais. Vous feriez mieux d'utiliser plutôt le plus moderne ScheduledExecutorService
(à partir de Java 5+).
Edit: La construction de base est:
ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(runnable, 0, 1000, TimeUnit.MILLISECONDS);
mais en y regardant de près, il n’ya aucun moyen d’annuler cette tâche une fois que celle-ci est lancée sans arrêter le service, ce qui est un peu étrange.
TimerTask
peut être plus facile dans ce cas, mais vous devrez créer une nouvelle instance lorsque vous en démarrerez une. Il ne peut pas être réutilisé.
Sinon, vous pouvez encapsuler chaque tâche en tant que service transitoire distinct:
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 solution la plus simple que j'ai trouvée: ajoutez simplement un booléen dans le code d'exécution de la tâche du minuteur, comme suit:
timer.schedule( new TimerTask() {
public void run() {
if(!paused){
//do your thing
}
}
}, 0, 1000 );
Si vous avez déjà annulé un chronomètre, vous ne pouvez pas le redémarrer, vous devrez en créer un nouveau.
Voir cette réponse , il contient une vidéo et le code source comment je fait quelque chose de similaire.
Il existe essentiellement deux méthodes: pause
et resume
En pause:
public void pause() {
this.timer.cancel();
}
En résumé:
public void resume() {
this.timer = new Timer();
this.timer.schedule( aTask, 0, 1000 );
}
Cela rend la perception de pause/reprise.
Si vos minuteries effectuent différentes actions en fonction de l'état de l'application, vous pouvez envisager d'utiliser le StatePattern
Le poing définit un état abstrait:
abstract class TaskState {
public void run();
public TaskState next();
}
Et fournissez autant d'états que vous le souhaitez. La clé est qu'un État vous mène à un autre.
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();
}
}
Et puis vous changez l'état dans votre minuterie.
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 );
Enfin, si vous avez besoin d'exécuter la même chose, mais à des vitesses différentes, vous pouvez envisager d'utiliser le paramètre TimingFramework . C'est un peu plus complexe mais vous pouvez faire des animations sympas, en permettant de peindre certains composants à des rythmes différents (au lieu d'être linéaires)
En examinant votre code source, voici les modifications (qui valident quasiment ma réponse précédente)
Dans task1:
// Stop timer1 and start timer2
timer1.cancel();
timer2 = new Timer(); // <-- just insert this line
timer2.scheduleAtFixedRate(new Task2(), 0, 1000);
et dans task2:
// Stop timer2 and start timer1
timer2.cancel();
timer1 = new Timer(); // <-- just insert this other
timer1.scheduleAtFixedRate(new Task1(), 0, 1000);
Il fonctionne sur ma machine:
Ça marche! http://img687.imageshack.us/img687/9954/capturadepantalla201001m.png
À mon avis, ceci est quelque peu erroné. Si votre code nécessite des garanties de temps, vous ne pouvez pas utiliser Timer de toute façon, et vous ne voudriez pas le faire. "Cette classe n'offre aucune garantie en temps réel: elle planifie les tâches à l'aide de la méthode Object.wait (long)."
La réponse, à mon humble avis, est que vous ne voulez pas mettre en pause et redémarrer vos minuteries. Vous voulez juste empêcher leurs méthodes d'exécution de faire leur affaire. Et c’est facile: vous n’avez qu’à les emballer dans une déclaration if
. Le commutateur est allumé, ils fonctionnent, le commutateur est éteint, ils ratent ce cycle.
Edit: / La question a considérablement changé par rapport à ce qu’elle était à l’origine, mais je laisserai cette réponse au cas où cela aiderait quelqu'un. Mon point est le suivant: si vous ne vous souciez pas du fait que votre événement se déclenche dans la tranche de N millisecondes (juste qu'il ne dépasse pas une fois toutes les N millisecondes), vous pouvez simplement utiliser des conditions sur les méthodes d'exécution. C’est en fait un cas très courant, surtout lorsque N est inférieur à 1 seconde.
Android ne réutilisera pas une tâche de minuterie qui a déjà été planifiée une fois. Il est donc nécessaire de réinitialiser les tâches Timer et TimerTask, par exemple comme ceci dans 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.
}
Je peux arrêter une minuterie et une tâche en utilisant le code suivant:
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;
}