JavaFX Beans绑定突然停止工作

13

我使用JavaFX NumberBindings来计算某些值。一开始一切正常,但是在很短的时间内,绑定就突然停止工作了。我也没有收到异常。

我尝试了几种绑定方法和高级、低级的方法。甚至覆盖计算本身也会突然停止,不再被调用。我也已经更新到最新的JDK(1.8.0_05),重新构建/重启了所有内容。

以下是最小化的工作示例,说明了问题所在。它应该向STDOUT输出主窗口的当前宽度。调整窗口大小约10秒后,输出就会停止。我还尝试将结果属性绑定到JavaFX控件,以确保属性的持续使用,但都无济于事。我相信我在这里缺少一些非常基本的Property/Bindings行为,Google似乎根本不知道这种行为。

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

}

我可以重现这个问题。看起来像是一个bug。你在jira上搜索过吗? - assylias
我似乎找不到现有的错误报告。这似乎是如此基础,很难想象它会是JDK/JRE本身的错误。 - underkuerbis
问题(就像James_D的答案一样)可以通过删除boundNumberProperty变量并直接将监听器添加到currentWidthPlusTen来简化。问题仍然存在,但解决方案仍然有效。 - bruno
1个回答

24

绑定使用WeakListener来观察currentWidthPlusTen的值。由于您没有保留对boundNumberProperty的引用,因此它在start(...)方法退出后就可以进行垃圾回收了。当垃圾收集器启动时,引用将完全丢失,绑定将不再起作用。

要直接查看此内容,请添加以下行:

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

将监听器绑定到start(...)方法。您可以通过单击窗口来强制监听器停止工作。
显然,这不是您想要的:解决方法是在start(...)退出后保留对boundNumberProperty的引用。例如:
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);
    }

}

更新

任何遇到这个问题的人可能也想看看Tomas Mikula的ReactFX,它提供了一个更清晰的解决方案(但需要使用第三方库,你需要花费一些时间学习)。Tomas在这篇博客随后的文章中解释了这个问题以及ReactFX如何解决它。


非常好!这解决了我的问题。我倾向于信任并依赖Java中的自动内存/引用管理。显然太多了:D - underkuerbis
有趣的是,大多数情况下,当你在实际应用中使用它时,超出了简单类型测试的范畴,这个问题就会消失。例如,如果你创建一个标签并将其文本绑定到你的IntegerProperty,那么这个标签有效地保持对该属性的引用,因此它不会被垃圾回收。只有偶尔在真实场景中会出现这个问题,但这种情况非常罕见。 - James_D
这个问题让我抓狂,因为我的大量代码在2.x中运行良好,现在却“神秘地”停止工作。希望至少能控制何时使用WeakListeners... - metasim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接