Ossama
Ossama

Reputation: 2433

How to properly exit javaFX Platform.runLater

The code below doest not exit properly when I close the application. I believe the issue is where do i exactly call the system.exit and platform.exit....

hour_Label.textProperty().bind(hour);
minute_Label.textProperty().bind(minute);
second_Label.textProperty().bind(second);

new Thread(() -> 
{    
    for (;;) 
    {                 
        try 
        { 
            final SimpleDateFormat simpledate_hour = new SimpleDateFormat("h");
            final SimpleDateFormat simpledate_minute = new SimpleDateFormat("mm");
            final SimpleDateFormat simpledate_second = new SimpleDateFormat("s");
            Platform.runLater(new Runnable() {
                @Override public void run() 
                {
                    hour.set(simpledate_hour.format(new Date()));
                    minute.set(simpledate_minute.format(new Date()));
                    second.set(simpledate_second.format(new Date())); 
                }
            });
            Thread.sleep(200);                
        }
        catch (Exception e){logger.warn("Unexpected error", e); Thread.currentThread().interrupt(); Platform.exit(); System.exit(0);}
    }
}).start();

Upvotes: 1

Views: 3732

Answers (2)

isnot2bad
isnot2bad

Reputation: 24444

Don't use threads! Use a Timeline instead:

Timeline clock = new Timeline(
    new KeyFrame(Duration.seconds(0), evt -> {
        LocalTime now = LocalTime.now();
        hour.set(String.format("%d", now.getHour()));            
        minute.set(String.format("%02d", now.getMinute()));            
        second.set(String.format("%d", now.getSecond()));            
    }),
    new KeyFrame(Duration.seconds(1))
);
clock.setCycleCount(Animation.INDEFINITE);
clock.play();

As a Timeline is run by the FX Application Thread, you don't need any synchronization via Platform.runLater(...). Apart from that, you can start and stop the timeline as desired, and it automatically terminates when the FX Application Thread stops.

Upvotes: 1

jewelsea
jewelsea

Reputation: 159406

Make your thread a daemon thread.

The Java Virtual Machine exits when the only threads running are all daemon threads.

You also need to let the thread know that it should exit.

import javafx.application.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

import java.util.Date;
import java.util.concurrent.atomic.AtomicBoolean;

public class Sleeper extends Application{
    @Override
    public void start(Stage stage) throws Exception {
        Label time = new Label();

        AtomicBoolean shuttingDown = new AtomicBoolean(false);

        Thread thread = new Thread(() -> {
            while (!shuttingDown.get() && !Thread.interrupted()) {
                Platform.runLater(() -> time.setText(new Date().toString()));
                try {
                    Thread.sleep(1_000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        });
        thread.setDaemon(true);
        thread.start();

        Button exit = new Button("Exit");
        exit.setOnAction(event -> {
            shuttingDown.set(true);
            thread.interrupt();
            Platform.exit();
        });

        stage.setScene(new Scene(new StackPane(time), 240, 40));
        stage.show();
    }
}

You don't need to invoke Platform.exit() or System.exit(0) in an exception handler of your thread.

You may find it more convenient to use a JavaFX Task. The task documentation explains methods of canceling a task.

But really, I wouldn't advise using another thread at all for your example, instead use a Timeline as exemplified in Sergey's five second wonder in his answer to: JavaFX periodic background task.

Upvotes: 4

Related Questions