需要澄清在 javafx 应用程序线程中更改数据 [英] Need clarification on changing data in javafx application thread

查看:74
本文介绍了需要澄清在 javafx 应用程序线程中更改数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在将我的一个项目迁移到 JavaFX 并开始遇到线程问题.我将附上一个简短的例子.经过大量搜索,我设法解决了问题.我无法在 fx 应用程序线程之外更改 tableView 数据.我将代码从使用 SwingWorker 切换到任务.

I've been migrating a project of mine to JavaFX and started running into thread issues. I'll attach a short example. After much searching I managed to sort out the problem. I can't change the tableView data outside of the fx application thread. I switched my code over from using SwingWorker to a Task.

起初,这一直有效,直到我向表的 observableList 添加了更改侦听器.然后我收到错误不在 FX 应用程序线程上;"

At first, that worked until I added a change listener to the table's observableList. I then received the error "Not on FX application thread;"

当我尝试更新标签的值时,错误发生在 onChanged 方法中.我通过将它包装在 Platform.runLater() 中解决了这个问题.

The error happened inside the onChanged method when I attempted to update a Label's value. I resolved this by wrapping it inside Platform.runLater().

我只是不明白为什么更改标签说它不在应用程序线程上.这是在哪个线程上运行的?另外,我是否通过使用任务将行正确添加到我的表中?在我的实际应用程序中,我可以添加 50k 行,因此为什么要单独的线程以免锁定 UI.

I'm just confused as to why changing the label says it wasn't on the application thread. On what thread was this running? Also, am I adding rows to my table correctly by using a task? In my actual application, I could be adding 50k rows hence why the separate thread so as to not lock up the UI.

public class Temp extends Application{  
    private ObservableList<String> libraryList = FXCollections.observableArrayList();

    public void start(Stage stage) {

        Label statusLabel = new Label("stuff goes here");

        TableView<String> table = new TableView<String>(libraryList);
        table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);

        TableColumn<String, String> col = new TableColumn<String, String>("Stuff");
        col.setCellValueFactory(cellData -> new ReadOnlyStringWrapper(cellData.getValue()));
        table.getColumns().add(col);

        libraryList.addListener(new ListChangeListener<String>() {
            public void onChanged(Change change) {
                // Problem was caused by setting the label's text (prior to adding the runLater)
                Platform.runLater(()->{
                    statusLabel.setText(libraryList.size()+" entries");
                });                 
            }               
        });

        // dummy stuff
        libraryList.add("foo");
        libraryList.add("bar");

        Button b = new Button("Press Me");
        b.setOnAction(new EventHandler<ActionEvent>() {
            public void handle(ActionEvent e) {
                FileTask task = new FileTask();
                new Thread(task).start();
            }
        });

        BorderPane mainBody = new BorderPane();

        mainBody.setTop(statusLabel);
        mainBody.setCenter(table);
        mainBody.setBottom(b);
        Scene scene = new Scene(mainBody);
        stage.setScene(scene);
        stage.show();       
    }


    class FileTask extends Task<Boolean>{

        public FileTask(){

        }

        protected Boolean call() throws Exception{

            Random rand = new Random();
            for(int i = 0; i < 5; i++) {
                String s = ""+rand.nextInt(Integer.MAX_VALUE);
                libraryList.add(s);
            }

            return true;
        }   
    }     

    public static void main(String[] args) {
        Application.launch(args);

    }
}

推荐答案

它按预期工作,您有应用程序线程和任务线程,它们看起来像这样:

It's working as expected, you have the application thread and the task thread, they kind of look like this:

App ------\ ----------------------
Task       \-label.setText() Exception

除了 App 线程之外,您不能对任何 UI 进行工作,因此添加 RunLater 可以做到这一点:

You can't do any UI work on anything but the App thread, so adding your RunLater does this:

App ----\ -------------/ RunLater(label.setText()) ----------
Task     \-add to list/

效果很好.有几种方法可以根据您的目的进行管理:

which works well. There are a few ways to manage this based on what you want to do:

  • 如果您想更新 Task 中的 Table 列表,您可以将 RunLater 调用移动到任务内部,而不是处理程序内部,这样它仍然会让您回到 App 线程.这样,如果您实际上在应用线程上,则无需在处理程序中调用 RunLater.
App ---\ -----------------------/ label.setText() ----------
Task    \-RunLater(add to list)/

  • 另一种选择是只使用一个 Task> 它将在另一个线程上运行,并返回将要添加的字符串的完整列表.如果您在任务中进行网络调用,获取项目列表,然后在将它们全部下载到表中后添加它们,那么这更有可能是您想要的.
  • App -----\ ------------------------------/ label.setText() ---/ add to table list-------
    Task      \-build list, update progress /- return final list /
    

    希望格式保持不变.

    这篇关于需要澄清在 javafx 应用程序线程中更改数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆