【发布时间】:2017-08-26 02:33:28
【问题描述】:
我有一些复杂的 Observable 结构,它们可能是也可能不是坏主意,但这不是这个问题的重点。
这些结构的问题在于它们会导致 UI 显示的 Observable 对象的大量失效。据我所知,当 JavaFX UI 显示某些内容时,它会在其上注册一个 ChangeListener,因此任何使用惰性求值的尝试都会消失。也就是说,使 observable 无效似乎是在告诉 UI 它可能已更改,这会导致 UI 立即请求它的值,迫使它立即进行评估。
所以,我有了通过Platform.runLater() 推迟失效的想法。
我创建了一个名为DeferredBinding 的类,它将所有内容都委托给一个包装好的Binding,除了invalidate() 方法,它推迟到稍后处理的JavaFX UI 线程。它似乎工作......我可以使无效一百次,它似乎实际上只处理一次无效。
但是,我以前没有见过这种模式,我担心它可能属于“尝试不错但想法不好”的类别。
那么,问题:这是一个坏主意吗?我特别担心引入其他依赖于DeferredBinding 的Observable 对象的错误。一旦Platform.runLater() 发生,他们会好吗?
package com.myapp.SAM.model.datastructures;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.beans.binding.Binding;
import javafx.beans.value.ChangeListener;
import javafx.collections.ObservableList;
/**
* Specialized binding that defers its invalidations to the JavaFX UI thread in a throttled manner. The idea being that, if invalidate() is called many times,
* it only basically happens once (when the UI thread gets to it).
*/
public class DeferredBinding<T> implements Binding<T> {
private static final Logger logger = Logger.getLogger(DeferredBinding.class.getName());
private final Binding<T> binding;
private final AtomicBoolean pendingInvalidation = new AtomicBoolean(false);
public DeferredBinding(Binding<T> binding) {
this.binding = binding;
}
@Override
public void addListener(ChangeListener<? super T> listener) {
binding.addListener(listener);
}
@Override
public void removeListener(ChangeListener<? super T> listener) {
binding.removeListener(listener);
}
@Override
public T getValue() {
return binding.getValue();
}
@Override
public void addListener(InvalidationListener listener) {
binding.addListener(listener);
}
@Override
public void removeListener(InvalidationListener listener) {
binding.removeListener(listener);
}
@Override
public boolean isValid() {
return binding.isValid();
}
/**
* Override logic for invalidate() method to defer invalidation to runLater. Throttle the invalidations so as not to floor the JavaFX UI thread with
* multiple calls
*/
@Override
public void invalidate() {
if (pendingInvalidation.getAndSet(true) == false) {
Platform.runLater(() -> {
// Signal that the UI is processing the pending invalidation, so any additional invalidations must schedule another update.
pendingInvalidation.set(false);
binding.invalidate();
});
}
}
@Override
public ObservableList<?> getDependencies() {
return binding.getDependencies();
}
@Override
public void dispose() {
binding.dispose();
}
}
【问题讨论】:
-
它类似于 Service 实现中使用的机制来避免更新淹没 UI,但是您有一个单独的线程,其中可能包含大量更新。我担心的是为什么你有一个结构,你需要在一个动作中一遍又一遍地更新相同的属性。
-
这是一个涉及聚合的复杂结构。我有一个
List,UI 需要不断地实时显示聚合和基于这些聚合的计算。问题是如果我需要更新List中的 60,000 个元素,聚合将重新计算 60,000 次(只有最后一个是真正相关的)。
标签: java javafx concurrency javafx-8