2014-05-21 14 views
9

Uso JavaFX NumberBindings per calcolare determinati valori. Inizialmente tutto funziona come previsto. Dopo un po 'di tempo, tuttavia, l'associazione smette di funzionare. Neanche io ricevo un'eccezione.JavaFX Beans improvvisamente smette di funzionare

Ho provato diverse associazioni, nonché approcci ad alto e basso livello. Anche il calcolo stesso (quando sottoposto a override) si ferma e non viene più chiamato. Ho anche aggiornato l'ultimo JDK (1.8.0_05) e ho ricostruito/riavviato tutto.

Il seguente esempio di lavoro minimo illustra il problema. Dovrebbe System.out.println la larghezza attuale della finestra principale a STDOUT. Dopo aver ridimensionato la finestra per circa 10 secondi, l'uscita si interrompe semplicemente. Ho anche cercato di associare la proprietà risultante a un controllo JavaFX, al fine di garantire l'utilizzo continuato della proprietà, ma ciò non ha avuto alcun risultato. Credo che mi manchi un comportamento molto basilare di Property/Bindings, Google sembra non conoscere questo comportamento.

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

Posso riprodurre il problema. Sembra un insetto. Hai cercato su jira? – assylias

+0

Non riesco a trovare segnalazioni di bug esistenti. Sembra essere qualcosa di così semplice, è difficile immaginare che si tratti di un bug nel JDK/JRE stesso. – underkuerbis

risposta

13

Il legame utilizza un WeakListener per osservare il valore di currentWidthPlusTen. Dal momento che non tieni un riferimento allo boundNumberProperty, esso è idoneo per la garbage collection non appena viene chiuso il metodo start(...). Quando il garbage collector entra in azione, il riferimento viene perso completamente e il binding non funziona più.

Per vedere questo direttamente, aggiungere la riga

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

al metodo start(...). Puoi forzare l'ascoltatore a "smettere di funzionare" facendo clic sulla finestra.

Ovviamente, questo non è ciò che si desidera: la correzione è di mantenere il riferimento a boundNumberProperty dopo le uscite start(...). Per esempio:

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

} 

Aggiornamento

Chiunque incorrere in questo problema potrebbe anche voler guardare di Tomas Mikula ReactFX, che fornisce una soluzione più pulita per questo (a scapito di utilizzare una libreria di terze parti , che avresti bisogno di dedicare un po 'di tempo all'apprendimento). Tomas spiega questo problema e come ReactFX lo risolve in this blog e nello subsequent post.

+5

+1 Wow ben individuato! – assylias

+0

Molto bene! Questo ha risolto il mio problema. Tendo a fidarmi e mi affido alla memoria automatica/gestione dei riferimenti in Java. Ovviamente troppo: D – underkuerbis

+0

È interessante notare che, la maggior parte delle volte in cui si utilizza effettivamente questo per qualcosa di reale, al di fuori dei semplici tipi di test nel codice di esempio, il problema scompare. Ad esempio, se si crea un'etichetta e si associa il proprio testo a "Proprietà Integer", ovviamente l'etichetta mantiene effettivamente un riferimento alla proprietà, in modo che non venga raccolta la garbage collection. Solo occasionalmente questo problema si presenta in uno scenario reale, ma è molto raro. –

Problemi correlati