【问题标题】:JavaFX running tasks on new threads when a button is clicked单击按钮时,JavaFX 在新线程上运行任务
【发布时间】:2019-10-09 19:34:44
【问题描述】:

当在 JavaFX 中单击按钮时,我正在尝试检索 XLS 文件并将其加载到 TableView 中。我正在使用 Task 类和 ExecutorService 来启动新线程。我需要阅读器类可重用,但 FileChooser 没有出现。

这是我编写一些并发代码的尝试。我想知道我做错了什么以及如何改进我的代码,因为一切都是事件驱动的?

控制器类代码

public void retrieveCustomersFromXLS() {
        try {
            loader.setOnSucceeded(workerStateEvent -> {
                File file = null;
                try {
                    file = loader.get();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                if (file != null && file.exists()) {
                    reader.setWorkingFile(file);
                    executor.submit(reader);
                }
            });
            reader.setOnSucceeded(workerStateEvent1 -> {
                Object[][] XLSFile = new Object[0][];
                try {
                    XLSFile = reader.get();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                if (XLSFile != null) {
                    tableInterface.setEntries(XLSFile);
                    tableInterface.setEntryType("customers");
                    executor.submit(tableInterface);
                }
            });
            tableInterface.setOnSucceeded(workerStateEvent2 -> {
                customerList = FXCollections.observableArrayList(tableInterface.getCustomerEntries());
                column_customerReference.setCellValueFactory(new PropertyValueFactory<customers, Integer>("customerReference"));
                column_customerName.setCellValueFactory(new PropertyValueFactory<customers, String>("customerName"));
                column_customerAddress.setCellValueFactory(new PropertyValueFactory<customers, String>("customerAddress"));
                column_customerPost.setCellValueFactory(new PropertyValueFactory<customers, Integer>("customerPost"));
                column_customerRegion.setCellValueFactory(new PropertyValueFactory<customers, String>("customerRegion"));
                column_customerID_DDV.setCellValueFactory(new PropertyValueFactory<customers, String>("customerDDV"));
                table_customerImports.setItems(customerList);
            });
            executor.submit(loader);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

阅读器类文件

@Override
    protected File call() throws Exception {
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("Choose Excel file");
        fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("Excel", "*.xlsx", "*.xls", "*.csv"));
        File selectedFile = fileChooser.showOpenDialog(new Stage());
        if (selectedFile != null) {
            path = selectedFile.getAbsolutePath();
            file = new File(path);
        }
        return file;
    }

【问题讨论】:

  • 只是我的建议,如果您不知道自己在做什么(而且您不知道),那么就不要编写并发代码。相反,只需为一个没有任何并发​​或执行器服务或任务或任何东西的线程编写,看看它是否可以接受。仅当它不执行时,才编写并发代码,但将其基于您已经拥有的工作实现。如果您不知道该怎么做,请粘贴非并发代码的 complete minimal reproducible example(没有 UI 表,因为它们与并发无关)并提出问题关于如何使其并发。
  • 在我进行任何 UI 或后台处理更改之前,我有一个工作的非并发示例,它非常不可读并阻塞了 UI 线程。我很清楚我不知道自己在做什么,但我想学习如何正确地制作并发程序。基本上我需要知道的是如何链接任务。
  • “基本上我需要知道的是如何链接任务”。您可以使用single thread executor。请参阅How to reset progress indicator between tasks in JavaFX2? 的答案。有关更简洁的示例,请参阅:How to queue tasks in JavaFX?

标签: java javafx concurrency


【解决方案1】:

您必须在 JavaFX 应用程序线程上调用 FileChooser#showXXXDialog 方法。如果您观察到您的任务失败,您会看到IllegalStateException 带有一条消息,说明您尝试在错误的线程上执行操作。此外,您不需要后台任务来提示用户首先输入文件。

这是一个提示用户输入文本文件的示例,读取Task 中的文本文件,并将结果放入ListView

App.java

package com.example;

import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class App extends Application {

  @Override
  public void start(Stage primaryStage) throws IOException {
    Scene scene = new Scene(FXMLLoader.load(getClass().getResource("/App.fxml")));
    primaryStage.setScene(scene);
    primaryStage.setTitle("FileChooser Example");
    primaryStage.show();
  }

}

App.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.Separator?>
<?import javafx.scene.layout.VBox?>

<VBox xmlns="http://javafx.com/javafx/13" xmlns:fx="http://javafx.com/fxml/1" spacing="5" prefWidth="600"
      prefHeight="400" alignment="CENTER" fx:controller="com.example.Controller">
    <padding>
        <Insets topRightBottomLeft="5"/>
    </padding>
    <Button text="Open File..." onAction="#handleOpenFile"/>
    <Separator/>
    <ListView fx:id="listView" VBox.vgrow="ALWAYS"/>
</VBox>

Controller.java

package com.example;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import javafx.collections.FXCollections;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.ListView;
import javafx.stage.FileChooser;
import javafx.stage.FileChooser.ExtensionFilter;

public class Controller {

  private final Executor executor = Executors.newSingleThreadExecutor(r -> {
    Thread t = new Thread(r, "controller-thread");
    t.setDaemon(true);
    return t;
  });

  @FXML private ListView<String> listView;

  @FXML
  private void handleOpenFile(ActionEvent event) {
    event.consume();

    FileChooser chooser = new FileChooser();
    chooser.getExtensionFilters()
        .add(new ExtensionFilter("Text Files", "*.txt", "*.json", "*.xml", "*.html", "*.java"));

    File file = chooser.showOpenDialog(listView.getScene().getWindow());
    if (file != null) {
      ReadFileTask task = new ReadFileTask(file.toPath());
      task.setOnSucceeded(wse -> listView.setItems(FXCollections.observableList(task.getValue())));
      task.setOnFailed(wse -> task.getException().printStackTrace());
      executor.execute(task);
    }
  }

  private static class ReadFileTask extends Task<List<String>> {

    private final Path file;

    private ReadFileTask(Path file) {
      this.file = Objects.requireNonNull(file);
    }

    @Override
    protected List<String> call() throws Exception {
      return Files.readAllLines(file);
    }

  }

}

【讨论】:

    猜你喜欢
    • 2018-04-14
    • 1970-01-01
    • 1970-01-01
    • 2017-08-08
    • 1970-01-01
    • 2014-12-22
    • 1970-01-01
    • 2015-10-22
    • 1970-01-01
    相关资源
    最近更新 更多