child stage listeners do not get killed after closing stage

Anu :

I have a thread that is running every 5 seconds inside my child stage. When I open and close child stage, this thread is kept on running. It should remove all the child's stage events and contents on close button click (cross at top right of screen). I am printing text on basis of TEXT inside child stage.

It prints as

NOT NULL

which means node is still present on the screen. It will affect app performance as I need to open and close these child stages frequently. Please advice, here is my code.

Main class:

public class dashboard extends Application {
@Override
public void start(Stage primaryStage) {

    BorderPane pane = new BorderPane();
    Button btn = new Button("Open child window");
    btn.setOnAction(new EventHandler<ActionEvent>() {
        public void handle(ActionEvent event) {                     
            final Stage dialog = new Stage();
            dialog.setTitle("Sensors Assignment");
            dialog.initModality(Modality.WINDOW_MODAL);
            dialog.initOwner(primaryStage);
            dialog.setResizable(false);

            VBox dialogVbox = (new childWindowClass()).GetChildContent();
            //dialogVbox.getChildren().add(closeButton);

            Scene dialogScene = new Scene(dialogVbox);
            dialog.setScene(dialogScene);
            dialog.showAndWait();

            dialog.setOnCloseRequest(e -> {System.out.println("Stage is closing");dialog.close();});        
        }
    });

    pane.setCenter(new VBox(new Text("Test 1234"), btn));

    ScrollPane scrollPane = new ScrollPane(pane);
    scrollPane.setFitToWidth(true);
    scrollPane.setHbarPolicy(ScrollBarPolicy.NEVER);
    scrollPane.setVbarPolicy(ScrollBarPolicy.AS_NEEDED);        

    Scene scene = new Scene(scrollPane);
    primaryStage.setTitle("test");       
    primaryStage.setMaximized(true);
    primaryStage.setScene(scene);
    primaryStage.show();
}

public static void main(String[] args) {
    launch(args);
}
}

Child class:

public class childWindowClass {
    private Text txt; private int _index = 0; SimpleDateFormat dateFrmt = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

    public VBox GetChildContent() {
        txt = new Text("Child Window Test " + _index);
        Label refreshClock = new Label(dateFrmt.format(new Date()));

        Thread timerThread = new Thread(() -> {             
            while (true) {
                try { Thread.sleep(5000);  }
                catch (InterruptedException e) {e.printStackTrace();}
                Platform.runLater(() -> {
                    refreshClock.setText(dateFrmt.format(new Date()));
                    try {

                        System.out.println(dateFrmt.format(new Date()) + "  -  " + (txt == null ? "" : " not") + " NULL");
                        if(txt != null)
                            txt.setText("Child Window Test " + _index);
                      _index++;
                    }
                    catch (Exception e) {e.printStackTrace();}                          
                });
            }
        });
        timerThread.start();
        return new VBox(txt, refreshClock);
    }
}
James_D :

For executing GUI-related code at fixed intervals in JavaFX, it's better to use the JavaFX API dedicated to this. For example, here you could use a Timeline: see, e.g. JavaFX periodic background task. You can easily call Timeline.stop() when you need it to stop.

Killing a running thread is a little more subtle. You need a boolean flag in your ChildWindowClass, and a method to set it. Then you can request the thread stops via a call to that method. You need to exercise care that changes to the flag made in one thread will be seen by the other; in this case it is sufficient to make the flag volatile. The following should work:

public class ChildWindowClass {
    private Text txt; 
    private int _index = 0; 
    private SimpleDateFormat dateFrmt = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

    private volatile boolean stopRequested ;

    public void requestStop() {
        stopRequested = true ;
    }

    public VBox getChildContent() {
        txt = new Text("Child Window Test " + _index);
        Label refreshClock = new Label(dateFrmt.format(new Date()));

        Thread timerThread = new Thread(() -> {  

            // update to stop thread when request is sent

            while (! stopRequested) {
                try { Thread.sleep(5000);  }
                catch (InterruptedException e) {e.printStackTrace();}
                Platform.runLater(() -> {
                    refreshClock.setText(dateFrmt.format(new Date()));
                    try {

                        System.out.println(dateFrmt.format(new Date()) + "  -  " + (txt == null ? "" : " not") + " NULL");
                        if(txt != null)
                            txt.setText("Child Window Test " + _index);
                      _index++;
                    }
                    catch (Exception e) {e.printStackTrace();}                          
                });
            }
        });
        timerThread.start();
        return new VBox(txt, refreshClock);
    }
}

And now you can do the following. Note that I changed some other code here; showAndWait() will actually wait until the dialog closes, so code after that can assume the dialog has closed.

btn.setOnAction(new EventHandler<ActionEvent>() {
    public void handle(ActionEvent event) {                     
        final Stage dialog = new Stage();
        dialog.setTitle("Sensors Assignment");
        dialog.initModality(Modality.WINDOW_MODAL);
        dialog.initOwner(primaryStage);
        dialog.setResizable(false);

        ChildWindowClass dialogUI = new ChildWindowClass();
        VBox dialogVbox = dialogUI.getChildContent();
        //dialogVbox.getChildren().add(closeButton);

        Scene dialogScene = new Scene(dialogVbox);
        dialog.setScene(dialogScene);
        dialog.setOnCloseRequest(e -> {
            System.out.println("Stage is closing");
            dialog.close();
        });        
        dialog.showAndWait();
        dialogUI.requestStop();
    }
});

This won't interrupt the thread, so it will continue in its current sleep state, but it is guaranteed to exit within five seconds of the dialog closing. If you need to kill it as soon as possible, you can retain a reference to it and call Thread.interrupt() when requestStop() is called, but that gets somewhat complicated. As I said at the beginning, it's better to use a Timeline and avoid threads completely for this kind of functionality.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=169327&siteId=1