Sh0ck
Sh0ck

Reputation: 97

Timer and calculating have multiple instances running

So this is the method i'm currently using to make the countdown work.

 /**
     * Starts the count-down
     * Triggers the handleFinish method
     * Sets the user under time pressure, which actually is a good way to provocate the user to start over again, to beat the score already achieved
     */
    public void startCountDown() {
        countDownText.setVisible(true);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                Platform.runLater(new Runnable() {
                    public void run() {
                        if(hasFinished)
                            return;
                        countDown--;
                        countDownText.setText("Time left:" + countDown);

                        if (countDown < 1)
                            handleFinish();
                    }
                });
            }
        }, 1000, 1000); //Every 1 second
    }

handleFinish method

/**
 * Action after the timer runs out
 * Disables the button, and sets a new highscore if the achieved score is greater than the current highscore
 * @param highScore
 */

public void handleFinish() {
    mainButton.setDisable(true);
    mainButton.setText("Disabled.");
    if(score > highScore) {
        highScore = score;
        saveData();
        notificationText.setText("Notification: New highscore! " + highScore);
        notificationText.setVisible(true);
        resetStats();
        countDownText.setVisible(false);
        System.out.println("New Highscore");
        return;
    } else {
    appearDialogue("You didn't beat your highscore! :( Try it again!");
    resetStats();
    }
    hasFinished = true;

}

resetStats method

 /**
 * Resets the stats
 * Changes the text of the button, sets the boolean and int @param hasStarted/ @param score to default (false) (0)
 * Sets the position of the button to the default one
 */
public void resetStats() {
    timer.purge();
    mainButton.setText("Click to start..");
    hasStarted = false;
    score = 0;
    mainButton.setLayoutX(201);
    mainButton.setLayoutY(157);
    countDown = 20;
    scoreText.setVisible(false);
    mainButton.setDisable(false);
    System.out.println("STATS RESET");
}

calculatePosition is also a timer (TimerTask)

My problem is now, that they start "multi-threading", like more and more instances of the objects. (Like instead, of one timer counting down, there are 2 instances running, so - 2 seconds every 1 second). Same goes with the calculate position problem.

instance.purge();

purge just resets the timer if i'm using

 instance.cancel();

it throws this error at me:

Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at javafx.fxml.FXMLLoader$MethodHandler.invoke(Unknown Source)
    at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(Unknown Source)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(Unknown Source)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(Unknown Source)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.EventUtil.fireEventImpl(Unknown Source)
    at com.sun.javafx.event.EventUtil.fireEvent(Unknown Source)
    at javafx.event.Event.fireEvent(Unknown Source)
    at javafx.scene.Scene$ClickGenerator.postProcess(Unknown Source)
    at javafx.scene.Scene$ClickGenerator.access$7900(Unknown Source)
    at javafx.scene.Scene$MouseHandler.process(Unknown Source)
    at javafx.scene.Scene$MouseHandler.access$1500(Unknown Source)
    at javafx.scene.Scene.impl_processMouseEvent(Unknown Source)
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Unknown Source)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(Unknown Source)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(Unknown Source)
    at com.sun.glass.ui.View.handleMouseEvent(Unknown Source)
    at com.sun.glass.ui.View.notifyMouse(Unknown Source)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$141(Unknown Source)
    at com.sun.glass.ui.win.WinApplication$$Lambda$37/1109371569.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at sun.reflect.misc.Trampoline.invoke(Unknown Source)
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at sun.reflect.misc.MethodUtil.invoke(Unknown Source)
    ... 31 more
Caused by: java.lang.IllegalStateException: Timer already cancelled.
    at java.util.Timer.sched(Unknown Source)
    at java.util.Timer.schedule(Unknown Source)
    at MainController.startCountDown(MainController.java:233)
    at MainController.start(MainController.java:91)
    ... 40 more

Somehow when this error occurs, the calculatePosition task does work normally (doesn't multiply itself)

Help is appreciated! :)

kind regards, Yasin

Upvotes: 0

Views: 417

Answers (1)

Jos&#233; Pereda
Jos&#233; Pereda

Reputation: 45456

Instead of a TimerTask you just can use a Timeline. Read more here.

This is my short version of your game:

private static final Integer TOTALTIME = 20;
private final IntegerProperty count = new SimpleIntegerProperty(TOTALTIME);

private final Label timerLabel = new Label();
private final Button btn = new Button("Start countdown");

@Override
public void start(Stage primaryStage) {

    timerLabel.textProperty().bind(count.asString());
    timerLabel.setVisible(false);

    final Timeline timeline = new Timeline();
    timeline.setCycleCount(TOTALTIME);
    KeyFrame keyFrame = new KeyFrame(Duration.seconds(1), e->count.set(count.get()-1));
    timeline.getKeyFrames().add(keyFrame);
    timeline.setOnFinished(e->handleFinish());

    btn.setOnAction(e->{
        btn.setDisable(true);
        timerLabel.setVisible(true);
        timeline.playFromStart();
    });

    VBox root = new VBox(20,timerLabel,btn);        
    Scene scene = new Scene(root, 300, 250);

    primaryStage.setScene(scene);
    primaryStage.show();
}

private void handleFinish() {
    timerLabel.setVisible(false);
    btn.setDisable(false);
    resetStats();
}

private void resetStats() {
    count.set(TOTALTIME);
}

Note that you don't need Platform.runLater(), since everything is executed on the JavaFX application thread.

Upvotes: 1

Related Questions