从另一个线程更新 JavaFX UI

2022-09-01 06:04:30

我有一个JavaFX应用程序和一个工作线程,通过实现,它执行一个漫长的过程,即压缩和上传一组文件。javafx.concurrent.Task

我已通过 将任务进度连接到进度条。progressProperty

除此之外,我还希望将有关正在处理的项的详细状态报告到 UI 中。也就是说,正在处理的文件的名称及其大小以及单个文件进程可能产生的任何错误。

使用这些信息更新 UI 无法从工作线程完成,最多我可以将其添加到同步集合中。

但是,我需要一些事件来通知 UI 新数据可用。

JavaFX 是否对此问题有一些特定的支持?

更新,更好的配方

我没有将一个临时的跨线程机制设计为 ,而是试图允许从其他线程侦听每个属性。就像.)一样和提供。Platform.runLaterrunningPropertystatePropertyTask


答案 1

我遇到了类似的问题,据我所知,您必须自己处理错误处理。我的解决方案是通过方法调用更新 UI:

像这样:

  try
  {
    //blah...
  }
  catch (Exception e)
  {
    reportAndLogException(e);
  }
  ...
  public void reportAndLogException(final Throwable t)
  {
    Platform.runLater(new Runnable() {
      @Override public void run() {
        //Update UI here     
      }
    });
  }

从本质上讲,我只是手动将其移回UI线程进行更新(就像我在几乎任何其他框架中所做的那样)。


答案 2

这个答案使用与丹尼尔的答案相同的概念。

随附的是 Task javadoc 中部分结果示例的副本(修复了当前嵌入在 Java 8 javadoc 中的语法错误并添加了更具体的泛型类型)。您可以使用修改。

将异常放在部分结果集合中。对于您的情况,您不需要从任务返回异常列表,而是可以将它们放在显示异常的某个 UI 控件中(如带有异常的显示)。请注意,部分Results集合不需要同步,因为它总是在JavaFX UI线程上更新和访问(更新是通过类似于Daniel的解决方案的调用进行的)。ListViewCellFactoryPlatform.runLater()

public class PartialResultsTask extends Task<ObservableList<Rectangle>> {
    private ReadOnlyObjectWrapper<ObservableList<Rectangle>> partialResults =
            new ReadOnlyObjectWrapper<>(
                    this, 
                    "partialResults",
                    FXCollections.observableArrayList(
                            new ArrayList<>()
                    )
            );

    public final ObservableList<Rectangle> getPartialResults() {
        return partialResults.get();
    }

    public final ReadOnlyObjectProperty<ObservableList<Rectangle>> partialResultsProperty() {
        return partialResults.getReadOnlyProperty();
    }

    @Override
    protected ObservableList<Rectangle> call() throws Exception {
        updateMessage("Creating Rectangles...");
        for (int i = 0; i < 100; i++) {
            if (isCancelled()) break;
            final Rectangle r = new Rectangle(10, 10);
            r.setX(10 * i);
            Platform.runLater(() -> partialResults.get().add(r));
            updateProgress(i, 100);
        }
        return partialResults.get();
    }
}

更新其可观察属性时,任务首先检查更新是否在 FX 应用程序线程上发生。如果是,它会立即更新。如果不是,则它将更新包装在调用中。请参阅任务源代码以了解如何完成此操作。Platform.runLater()

也许可以定义一组通用并发感知属性,但 JavaFX 的核心不提供此类功能。事实上,它不需要。除了javafx.concurrent包之外,JavaFX是一个单线程UI框架。