0

If I have a class with methods:

class Car{
    private String engineRPM;

    public Car(){}

    public String idleEngine(){
        if (engineOn()){
            engineRPM = getEngineRPM();
        }
        return engineRPM;
    }
}

How do I run the method in a task on a background thread in Main and then take its returned value and update the GUI?

public class Main extends Application {

Car myCar;

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

}

@Override
public void start(Stage primaryStage) throws Exception{
    FXMLLoader loader = new FXMLLoader(getClass().getResource("fxml/entry.fxml"));
    Parent root = loader.load();
    this.mainController = (SystemMessage) loader.getController();
    primaryStage.setTitle("Car");
    primaryStage.setScene(new Scene(root, 300, 275));
    primaryStage.show();

    //perform background task of class method here
    myCar = new Car();
    myCar.startEngine();
    String RPM = myCar.engineIdle(); //
    this.mainController.postMessage(RPM);

}
silversunhunter
  • 1,219
  • 2
  • 12
  • 32
  • you can use the Platform.runLater(Runnable...) to do it. Please check this: https://stackoverflow.com/questions/13784333/platform-runlater-and-task-in-javafx – Serge Aug 16 '17 at 20:46
  • 1
    @Serge `Platform.runLater(...)` runs something on the FX Application Thread, not on a background thread. So I guess you mean by this the UI update at the end should use `Platform.runLater(...)`. That is not clear from the comment. – James_D Aug 16 '17 at 21:15
  • @James_D yes, this is what i meant. Sorry for the truncated note. – Serge Aug 16 '17 at 23:56

1 Answers1

1

The basic idea is

  • Create a Task whose call() method does whatever you need to do in the background thread
  • If you have something to execute on the FX Application Thread when the task finishes, use the onSucceeded handler, which is invoked (on the FX Application Thread) after the call() method completes
  • When the call() method finishes, whatever is returned from it is set as the task's value, so you can call getValue() to see what was returned
  • Create a thread from the task, and call start() to execute it

So in your case, this just looks like

public class Main extends Application {


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

    }

    @Override
    public void start(Stage primaryStage) throws Exception{
        FXMLLoader loader = new FXMLLoader(getClass().getResource("fxml/entry.fxml"));
        Parent root = loader.load();
        SystemMessage mainController = loader.getController();
        primaryStage.setTitle("Car");
        primaryStage.setScene(new Scene(root, 300, 275));
        primaryStage.show();

        Task<String> carTask = new Task<String>() {
            @Override
            protected String call() throws Exception {
                Car myCar = new Car();
                myCar.startEngine();
                return myCar.engineIdle();
            }
        };
        carTask.setOnSucceeded(e -> mainController.postMessage(carTask.getValue()));
        new Thread(carTask).start();

    }

}

See the documentation for Task: it has plenty of examples.

James_D
  • 201,275
  • 16
  • 291
  • 322
  • This is starting to click!!! Thank you. One more question. If I want to run several methods inside the task could I update the GUI with the response of each one with the `postMessage` method I have? or do I need to figure out how to use `updateMessage()` – silversunhunter Aug 16 '17 at 21:47
  • @silversunhunter If there is just one `String` you are changing, you can call `updateMessage()`, and then bind your UI control to the task's message, or register a listener with the task's `messageProperty()`. More generally, you can perform progressive UI updates by putting the calls that update the UI in your `call()` method, each one wrapped in `Platform.runLater()`. Again, see the [documentation](http://docs.oracle.com/javase/8/javafx/api/javafx/concurrent/Task.html), e.g. the example "A Task Which Modifies The Scene Graph". – James_D Aug 16 '17 at 21:51