Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaFX Bindings.size() stops working after some events

I wrote a small runnable JavaFX application with two ways, to listen on size changes of an ObservableList. The first listener (line 52-58) works correct. The second listener (line 60-66) stops working after some events.

You can reproduce this error(?) when you often click the "Add button". The first view clicks, both messages were printed and some clicks later only the first listener further works.

Runnable example:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;


public class Main extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        final ObservableList<String> list = FXCollections.observableArrayList();
        primaryStage.setTitle("Demonstrator");

        // Button
        Button addButton = new Button();
        addButton.setText("Add Element");
        addButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                list.add("TEST");
            }
        });

        // ListView
        ListView<String> lv = new ListView<String>();
        lv.setItems(list);

        // Add elements to root
        VBox root = new VBox();
        root.getChildren().add(addButton);
        root.getChildren().add(lv);

        // Show scene
        primaryStage.setScene(new Scene(root, 300, 250));
        primaryStage.show();

        // This listener works correct
        list.addListener(new ListChangeListener<String>() {
            @Override
            public void onChanged(Change<? extends String> c) {
                System.out.println("#listener1: " + list.size());
            }
        });

        // This listener stops working after 10-30 clicks on the button
        Bindings.size(list).addListener(new ChangeListener<Number>() {
            @Override
            public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
                System.out.println("#listener2: " + newValue);
            }
        });
    }
}

I tried it with jdk-8u112-windows-x64 and jdk-8u112-windows-i586

Does anybody have an idea, if there is any mistake in my code, or if it's really a problem with JavaFX?

I got the solution:

The binding was removed from the garbage collector. You have to store the binding as field.

Thanks!

like image 354
Markus Mangei Avatar asked Dec 15 '16 22:12

Markus Mangei


1 Answers

After the line 61 is executed. The instance created in Bindings.size() is not strongly reachable by anything, so it is eligible for garbage collection. It is softly reachable through the listener, but that will not prevent garbage collection.

So eventually it will get garbage collected and stop working.

The solution is: make it strongly reachable, e.g. by storing it in a field in the class.

like image 63
k5_ Avatar answered Oct 21 '22 23:10

k5_