La unión de JavaFX Beans repentinamente deja de funcionar

Yo uso JavaFX NumberBindings para calcular ciertos valores. Inicialmente todo funciona como se esperaba. Sin embargo, después de una cantidad bastante pequeña de tiempo, el enlace simplemente deja de funcionar. Yo tampoco recibo una excepción.

Probé varios enlaces, así como enfoques de alto y bajo nivel. Incluso el cálculo en sí mismo (cuando se reemplaza) simplemente se detiene y ya no se llama. También actualicé al último JDK (1.8.0_05) y reconstruí / reinicié todo.

El siguiente ejemplo de trabajo mínimo ilustra el problema. Debería System.out.println el ancho actual de la ventana principal a STDOUT. Después de cambiar el tamaño de la ventana durante aproximadamente 10 segundos, la salida simplemente se detiene. También intenté vincular la propiedad resultante a un control JavaFX, para garantizar el uso continuado de la propiedad, pero eso fue inútil. Creo que me falta algo del comportamiento básico de la propiedad / enlaces aquí, Google no parece saber ese comportamiento en absoluto.

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() { @Override public void changed(ObservableValue observable, Number oldValue, Number newValue) { System.out.println(newValue.toString()); } }); } public static void main(String[] args) { launch(args); } } 

El enlace utiliza un WeakListener para observar el valor de currentWidthPlusTen . Como no guarda una referencia a boundNumberProperty , es elegible para la recolección de basura tan pronto como se boundNumberProperty el boundNumberProperty start(...) . Cuando el recolector de basura entra en acción, la referencia se pierde por completo y el enlace ya no funciona.

Para ver esto directamente, agrega la línea

 root.setOnMousePressed( event -> System.gc()); 

al método de start(...) Puede obligar al oyente a “dejar de trabajar” haciendo clic en la ventana.

Obviamente, eso no es lo que quieres: la solución es retener la referencia a boundNumberProperty después de que start(...) sale. Por ejemplo:

 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, 300, 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() { @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); } } 

Actualizar

Cualquiera que se encuentre con este problema también podría querer ver ReactFX de Tomás Mikula, que proporciona una solución más limpia para esto (a expensas de usar una biblioteca de terceros, que necesitaría dedicar algo de tiempo al aprendizaje). Tomas explica este problema y cómo ReactFX lo resuelve en este blog y en la publicación posterior .