在过去,我使用以下支持类将现有的ObservableValue
换成一个触发FX事件线程更改的内容......这样可以正常工作。
class AsyncBinding<T> implements ObservableValue<T> {
private List<InvalidationListener> invalidationListeners = new ArrayList<>(1);
private List<ChangeListener<? super T>> changeListeners = new ArrayList<>(1);
private ObservableValue<T> original;
private ChangeListener<T> changeListener;
private InvalidationListener invalidationListener;
public AsyncBinding(ObservableValue<T> original) {
super();
this.original = original;
changeListener = (obs, oldValue, newValue) -> {
Runnable job = () -> {
for (ChangeListener<? super T> listener : changeListeners) {
listener.changed(obs, oldValue, newValue);
}
};
Platform.runLater(job);
};
original.addListener(changeListener);
invalidationListener = obs -> {
Runnable job = () -> {
for (InvalidationListener listener : invalidationListeners) {
listener.invalidated(obs);
}
};
Platform.runLater(job);
};
original.addListener(invalidationListener);
}
@Override
public void addListener(InvalidationListener arg0) {
invalidationListeners.add(arg0);
}
@Override
public void removeListener(InvalidationListener arg0) {
invalidationListeners.remove(arg0);
}
@Override
public void addListener(ChangeListener<? super T> arg0) {
changeListeners.add(arg0);
}
@Override
public void removeListener(ChangeListener<? super T> arg0) {
changeListeners.remove(arg0);
}
@Override
public T getValue() {
return original.getValue();
}
}
但是,如果它与中间StringBinding
一起使用,则会停止更新。以下案例显示了不同的行为。我排除了垃圾收集。我只是在解释/理解不同的行为之后。
public class Case1 extends Application {
public static void main(String[] args) {
launch(args);
}
// hold references to prevent garbage collection...
private AsyncBinding<String> asyncValue;
@Override
public void start(Stage primaryStage) throws Exception {
Label label = new Label();
primaryStage.setScene(new Scene(label));
primaryStage.show();
ObjectProperty<String> property = new SimpleObjectProperty<>();
asyncValue = new AsyncBinding<>(property);
label.textProperty().bind(asyncValue);
new Thread(() -> {
for (int i = 0; i < 1_000_000; i++) {
property.set("a" + i);
}
}, "background").start();
}
}
<案例2 - 案例 - &gt; string - &gt;异步 - &gt;标签。停止随机更新......
public class Case2 extends Application {
public static void main(String[] args) {
launch(args);
}
// hold references to prevent garbage collection...
private StringBinding stringBinding;
private AsyncBinding<String> asyncValue;
@Override
public void start(Stage primaryStage) throws Exception {
Label label = new Label();
primaryStage.setScene(new Scene(label));
primaryStage.show();
ObjectProperty<String> property = new SimpleObjectProperty<>();
stringBinding = Bindings.createStringBinding(() -> {
return "b" + property.get();
}, property);
asyncValue = new AsyncBinding<>(stringBinding);
label.textProperty().bind(asyncValue);
new Thread(() -> {
for (int i = 0; i < 1_000_000; i++) {
property.set("a" + i);
}
}, "background").start();
}
}
<案例3 - 案例 - &gt;异步 - &gt; string - &gt;标签。工作正常。
public class Case3 extends Application {
public static void main(String[] args) {
launch(args);
}
// hold references to prevent garbage collection...
private StringBinding stringBinding;
private AsyncBinding<String> asyncValue;
@Override
public void start(Stage primaryStage) throws Exception {
Label label = new Label();
primaryStage.setScene(new Scene(label));
primaryStage.show();
ObjectProperty<String> property = new SimpleObjectProperty<>();
asyncValue = new AsyncBinding<>(property);
stringBinding = Bindings.createStringBinding(() -> {
return "b" + property.get();
}, asyncValue);
label.textProperty().bind(stringBinding);
new Thread(() -> {
for (int i = 0; i < 1_000_000; i++) {
property.set("a" + i);
}
}, "background").start();
}
}
答案 0 :(得分:1)
经过一些检查后,我发现问题似乎是因为java在一段时间后为后台线程创建了AsyncBinding.original
绑定的本地副本。后台线程不断更新它的本地副本,但JavaFX应用程序线程的值保持不变。
要解决此问题,您可以使用ChangeListener
中获得的值并将其指定给字段。请改为使用getValue
方法返回此值:
// field with updated value on the javafx application thread
private T value;
public AsyncBinding(ObservableValue<T> original) {
super();
this.original = original;
changeListener = (obs, oldValue, newValue) -> {
Runnable job = () -> {
value = newValue; // make sure the value on the application thread is the new value
for (InvalidationListener listener : invalidationListeners) {
listener.invalidated(obs);
}
for (ChangeListener<? super T> listener : changeListeners) {
listener.changed(obs, oldValue, newValue);
}
};
Platform.runLater(job);
};
original.addListener(changeListener);
}
...
@Override
public T getValue() {
return value;
}
这种方式应该更新 最终 。这可能需要一段时间,因为您正在进行1000000次Platform.runLater
次调用,这可能需要一段时间才能完成冻结应用程序,直到完成为止。