【问题标题】:How to set a Var from inside a Task?如何从任务内部设置 Var?
【发布时间】:2013-11-03 17:06:26
【问题描述】:

首先,我是 JavaFX 的新手,如果这个问题很愚蠢,我很抱歉。如何从任务中获取返回对象?

这是我的问题:

我想从 Mock 中获取对象列表。 Mock 有 1 到 5 秒的延迟。但我不希望我的 GUI 在这个时候冻结。

在 Java.Swing 中,使用线程很容易,但据我所知,JavaFX 有任务。

我已经阅读了很多教程,但是它们到处都返回一个文本属性。所以这是我的问题:如何使用任务/线程(在我的情况下为列表)的计算结果设置对象的值

谢谢。

【问题讨论】:

    标签: java multithreading javafx-2 javafx task


    【解决方案1】:

    雷,

    您是对的,这些示例似乎掩盖了从任务中获取结果。有两种方法可以取回我知道的结果:

    1. 通过Task类的getValue()方法(我就是这样做的)
    2. 通过父 FutureTask 类的get() 方法(我没有使用过,但原则上应该可以)。

    使用第一个getValue() 方法,您需要确保任务通过任务调用方法中的updateValue(...) 方法设置值。然后在WorkerStateEvent上放一个监听器

    myTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
    
        @SuppressWarnings("unchecked")
        @Override
        public void handle(WorkerStateEvent event) {
            ReturnType rt =  (ReturnType) event.getSource().getValue()
            // ... other stuff to do here ...
        }
    });
    

    第一种方法有点冗长,但它可以在任务完成后进行一些更复杂的操作。

    第二种方法更简单直接,但不能让您对任务完成时的操作进行太多控制。使用 FutureTask 的 get() 方法,代码应该阻塞,直到 Task 返回值。所以使用它应该很简单:

    //
    // Start the task in a thread (using whatever approach you like) 
    //before calling the get() method.
    //
    ReturnType rt = myTask.get();
    

    我已经将Future 对象与其他代码一起使用,但我没有将FutureTask 与FX api 一起使用,因此我无法告诉您其中是否存在隐藏的陷阱。

    祝你好运,

    小鸡

    【讨论】:

    • 感谢您的回答,我自己发现了一个错误。如果任务完成,我忘记添加一个 EventHandler 。所以我总是调用 task.getValue() 但任务仍在运行,所以值为 null。
    【解决方案2】:

    Task 是一个泛型类型。这意味着如果您将类型应用于像Task&lt;Integer&gt; 这样的任务,则任务类将具有返回整数的函数。其中一个功能是valueProperty(),可以绑定到其他场景元素。显示在标签或其他内容中。我建议你阅读the javafx binding tutorial,以获得更好的理解。

    这里是任务使用或其属性的示例。

    import javafx.application.Application;
    import javafx.beans.value.ChangeListener;
    import javafx.beans.value.ObservableValue;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.concurrent.Task;
    import javafx.event.ActionEvent;
    import javafx.event.EventHandler;
    import javafx.geometry.Pos;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.control.Label;
    import javafx.scene.control.ListView;
    import javafx.scene.control.ProgressBar;
    import javafx.scene.layout.HBox;
    import javafx.scene.layout.VBox;
    import javafx.stage.Stage;
    
    public class TestingTasks extends Application{
    
        public static void main(String[] args) {launch(args);}
    
        @Override
        public void start(Stage stage) throws Exception {
    
            VBox vbox = new VBox(10);
            vbox.setAlignment(Pos.TOP_CENTER);
    
            ListView<String> list = new ListView<>();
            HBox hbox = new HBox(10);
            hbox.setAlignment(Pos.CENTER_LEFT);
            Label labelMessage = new Label();
            hbox.getChildren().addAll(new Label("Message: "), labelMessage);
            ProgressBar progress = new ProgressBar(-1);
            progress.setVisible(false);
    
            Button button = new Button("Executing Task");
            button.setOnAction(event(button, list, progress, labelMessage));
    
            vbox.getChildren().addAll(list, hbox, button, progress);
            Scene scene = new Scene(vbox, 400, 300);
            stage.setScene(scene);
            stage.show();
        }
    
        private EventHandler<ActionEvent> event(final Button button, final ListView<String> list, final ProgressBar progress, final Label labelMessage) {       
            return new EventHandler<ActionEvent>() {
                @Override
                public void handle(ActionEvent event) {
                    Task<ObservableList<String>> task = generateTask();
                    list.itemsProperty().bind(task.valueProperty());
                    progress.visibleProperty().bind(task.runningProperty());
                    labelMessage.textProperty().bind(task.messageProperty());
                    button.disableProperty().bind(task.runningProperty());
                    task.runningProperty().addListener(listenerRunningTask());
                    Thread t = new Thread(task);
                    t.setDaemon(true);
                    t.start();
                }
    
            };
        }
    
        private Task<ObservableList<String>> generateTask() {               
            return new Task<ObservableList<String>>() {                 
                @Override
                protected ObservableList<String> call() throws Exception {
                    updateMessage("Waiting...");
                    Thread.sleep(5000);
                    updateMessage("Waking up");
                    return FXCollections.observableArrayList("One", "Two", "Three");
                }
            };
        }   
    
        private ChangeListener<? super Boolean> listenerRunningTask() {     
            return new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
                    if(oldValue && !newValue){
                        //TODO when finish
                    }               
                }
            };
        }   
    }
    

    所以基本上,您可以在任务中返回一个变量,或者等待任务结束并执行某些操作,创建自己的绑定...

    如果您想从线程修改屏幕的某些内容,则需要从 FX 线程进行,Task 函数调用位于 FX 线程之外,因此屏幕不会冻结。但是所有的绑定元素都会发生在 FX Thread 中,所以修改 GUI 是安全的。

    如果您想从非 FX 线程安全地修改 GUI,只需执行以下操作:

    Platform.runLater(new Runnable() {                  
        @Override
        public void run() {
            //Safe modification in the FX Thread
        }
    });
    

    还可以查看concurrency in JavaFX2。这更深入地解释了并发、Service、Task...

    希望对你有帮助!

    【讨论】:

      猜你喜欢
      • 2021-09-26
      • 1970-01-01
      • 2018-07-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多