2014-05-21 35 views
9

Używam JavaFX NumberBindings w celu obliczenia pewnych wartości. Początkowo wszystko działa zgodnie z oczekiwaniami. Jednak po niedługim czasie wiązanie przestaje działać. Nie dostaję też wyjątku.JavaFX Beans Binding nagle przestaje działać

Próbowałem już kilku wiązań, a także podejścia wysokiego i niskiego poziomu. Nawet samo obliczenie (gdy zostanie zastąpione) po prostu się zatrzymuje i nie jest już wywoływane. Zaktualizowałem także najnowszą wersję JDK (1.8.0_05) i przebudowałem/zrestartowałem wszystko.

Poniższy minimalny przykład roboczy ilustruje problem. Powinno to System.out.println bieżącej szerokości okna głównego do STDOUT. Po zmianie rozmiaru okna na około 10 sekund, wyjście po prostu się zatrzymuje. Próbowałem również powiązać wynikową właściwość z formantem JavaFX, aby zapewnić dalsze użytkowanie Nieruchomości, ale to było bezskuteczne. Sądzę, że brakuje mi bardzo podstawowego zachowania własności/powiązań tutaj, Google wydaje się nie znać tego zachowania w ogóle.

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); 
} 

} 
+0

Mogę odtworzyć problem. Wygląda jak błąd. Szukałeś w jira? – assylias

+0

Nie mogę znaleźć istniejących raportów o błędach. Wydaje się być czymś tak podstawowym, że trudno wyobrazić sobie, że jest to błąd w samym JDK/JRE. – underkuerbis

Odpowiedz

13

wiązania wykorzystuje WeakListener obserwować wartość currentWidthPlusTen. Ponieważ nie zachowujesz odniesienia do boundNumberProperty, jest ono uprawnione do usuwania śmieci, gdy tylko metoda start(...) zostanie zamknięta. Kiedy robot zbiera śmieci, odniesienie zostaje całkowicie utracone, a wiązanie już nie działa.

Aby to zobaczyć bezpośrednio, dodaj linię

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

metody start(...). Możesz zmusić słuchacza do "przestania działać", klikając okno.

Oczywiście, nie tego chcesz: poprawka ma zachować odniesienie do boundNumberProperty po wyjściu z start(...). Na przykład:

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<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); 
    } 

} 

aktualizacji

Każdy działa w tej kwestii może również zajrzeć do Tomas Mikula na ReactFX, który zapewnia czystsze obejście tego (kosztem przy użyciu biblioteki innej firmy , które trzeba poświęcić na naukę). Tomas wyjaśnia ten problem i sposób, w jaki ReactFX rozwiązuje go w this blog i subsequent post.

+5

+1 Wow dobrze zauważony! – assylias

+0

Bardzo dobrze! To rozwiązało mój problem. Mam zaufanie i polegam na automatycznym zarządzaniu pamięcią/referencjami w Javie. Oczywiście za dużo: D – underkuerbis

+0

Co ciekawe, w większości przypadków używa się tego do czegoś rzeczywistego, poza prostymi typami testów w przykładowym kodzie, problem znika. Na przykład, jeśli utworzysz etykietę i powiązasz jej tekst z 'IntegerProperty', to oczywiście etykieta skutecznie zachowa odwołanie do właściwości, więc nie będzie zbierać śmieci. Od czasu do czasu problem ten pojawia się w rzeczywistym scenariuszu, ale jest bardzo rzadki. –