JavaFX bean binding suddenly stops working
I use JavaFX numberbindings to calculate certain values At first, everything worked as expected However, after a fairly short time, the binding just stops working I didn't get any exceptions either
I tried several bindings, as well as high - and low - level methods Even if the calculation itself (when overridden) just stops and is no longer called I also updated to the latest JDK (1.8.0_05) and rebuilt / restarted everything
The following minimum working example illustrates this problem System. out. Println sets the current width of the main window to stdout After adjusting the window for about 10 seconds, the output will stop I also tried to bind the result property to a JavaFX control to ensure that the property continues to be used, but it didn't help I believe I missed some very basic property / binding behavior here, which Google doesn't seem to know
import javafx.application.Application; import javafx.beans.binding.NumberBinding; import javafx.beans.property.IntegerProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.value.changelistener; import javafx.beans.value.ObservableValue; import javafx.scene.Scene; import javafx.scene.layout.StackPane; import javafx.stage.Stage; public class BindingsProblem extends Application { @Override public void start(Stage primaryStage) { // Initialization... StackPane root = new StackPane(); Scene scene = new Scene(root,300,250); primaryStage.setScene(scene); primaryStage.show(); // Binding - The problem occurrs here! NumberBinding currentWidthPlusTen = primaryStage.widthproperty().add(10); IntegerProperty boundNumberProperty = new SimpleIntegerproperty(); boundNumberProperty.bind(currentWidthPlusTen); boundNumberProperty.addListener(new changelistener<Number>() { @Override public void changed(ObservableValue<? extends Number> observable,Number oldValue,Number newValue) { System.out.println(newValue.toString()); } }); } public static void main(String[] args) { launch(args); } }
Solution
The binding uses the weaklistener to observe the value of currentwidth plusten Since you do not retain a reference to boundnumberproperty, garbage collection can be used once the start (...) method exits When the garbage collector starts, the reference is completely lost and the binding is no longer valid
To view directly, add a row
root.setOnMousePressed( event -> System.gc());
To... (...) method You can force the listener to "stop working" by clicking on the window
Obviously, this is not what you want: the fix is to keep the reference to boundnumberproperty after start (...) exits For example:
import javafx.application.Application; import javafx.beans.binding.NumberBinding; import javafx.beans.property.IntegerProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.value.changelistener; import javafx.beans.value.ObservableValue; import javafx.scene.Scene; import javafx.scene.layout.StackPane; import javafx.stage.Stage; public class BindingsProblem extends Application { IntegerProperty boundNumberProperty; @Override public void start(Stage primaryStage) { // Initialization... StackPane root = new StackPane(); Scene scene = new Scene(root,250); primaryStage.setScene(scene); primaryStage.show(); // Binding - The problem occurrs here! NumberBinding currentWidthPlusTen = primaryStage.widthproperty() .add(10); boundNumberProperty = new SimpleIntegerproperty(); boundNumberProperty.bind(currentWidthPlusTen); boundNumberProperty.addListener(new changelistener<Number>() { @Override public void changed(ObservableValue<? extends Number> observable,Number newValue) { System.out.println(newValue.toString()); } }); } public static void main(String[] args) { launch(args); } }
to update
Anyone who encounters this problem may also want to see Tomas mikula's reactfx, which provides a cleaner solution (at the expense of using a third-party library, you need to spend some time learning) Tomas explained this problem and how reactfx solves it in this blog and subsequence post