Asked  6 Months ago    Answers:  5   Viewed   38 times

I have been doing some research on this but I am still VERY confused to say the least.

Can anyone give me a concrete example of when to use Task and when to use Platform.runLater(Runnable);? What exactly is the difference? Is there a golden rule to when to use any of these?

Also correct me if I'm wrong but aren't these two "Objects" a way of creating another thread inside the main thread in a GUI (used for updating the GUI)?

 Answers

85

Use Platform.runLater(...) for quick and simple operations and Task for complex and big operations .

  • Use case for Platform.runLater(...)
  • Use case for Task: Task Example in Ensemble App

Example: Why Can't we use Platform.runLater(...) for long calculations (Taken from below reference).

Problem: Background thread which just counts from 0 to 1 million and update progress bar in UI.

Code using Platform.runLater(...):

final ProgressBar bar = new ProgressBar();
new Thread(new Runnable() {
    @Override public void run() {
    for (int i = 1; i <= 1000000; i++) {
        final int counter = i;
        Platform.runLater(new Runnable() {
            @Override public void run() {
                bar.setProgress(counter / 1000000.0);
            }
        });
    }
}).start();

This is a hideous hunk of code, a crime against nature (and programming in general). First, you’ll lose brain cells just looking at this double nesting of Runnables. Second, it is going to swamp the event queue with little Runnables — a million of them in fact. Clearly, we needed some API to make it easier to write background workers which then communicate back with the UI.

Code using Task :

Task task = new Task<Void>() {
    @Override public Void call() {
        static final int max = 1000000;
        for (int i = 1; i <= max; i++) {
            updateProgress(i, max);
        }
        return null;
    }
};

ProgressBar bar = new ProgressBar();
bar.progressProperty().bind(task.progressProperty());
new Thread(task).start();

it suffers from none of the flaws exhibited in the previous code

Reference : Worker Threading in JavaFX 2.0

Tuesday, June 1, 2021
 
Octopus
answered 6 Months ago
47

You do not have the rights to kill other processes; hence, killProcess() does not work for your app.

Friday, July 30, 2021
 
jab
answered 4 Months ago
jab
95

use DataFX,which will make your job easier :)

Example Code :

DataSourceReader dsr1 = new FileSource("your csv file path");
String[] columnsArray // create array of column names you want to display 
CSVDataSource ds1 = new CSVDataSource(dsr1,columnsArray);
TableView tableView = new TableView();
tableView.setItems(ds1.getData());
tableView.getColumns().addAll(ds1.getColumns());

Reference : Introduction to DataFX

Edit : Standard JavaFX Way

replace your code :

for(List<String> dataList : data) {
    table1.setItems(dataList); // Requires an ObservableList!
}

with

  //  which will make your table view dynamic 
 ObservableList<ObservableList> csvData = FXCollections.observableArrayList(); 

 for(List<String> dataList : data) {
     ObservableList<String> row = FXCollections.observableArrayList();
    for( String rowData : dataList) {
      row.add(rowData); 
  }
   cvsData.add(row); // add each row to cvsData
}

table1.setItems(cvsData); // finally add data to tableview
Thursday, August 5, 2021
 
commonpike
answered 4 Months ago
49

Add a property in the FileDownloader class representing the progress. For example, if you wanted to expose the number of bytes downloaded, you could do

public class FileDownloader {

    private final ReadOnlyLongWrapper bytesDownloaded = new ReadOnlyLongWrapper();

    public final long getBytesDownloaded() {
        return bytesDownloaded.get();
    }

    public final ReadOnlyLongProperty bytesDownloadedProperty() {
        return bytesDownloaded.getReadOnlyProperty();
    }

    private long totalBytes ;

    public long getTotalBytes() {
        return totalBytes ;
    }

    // other code as before...

    public boolean downloadSysVersion() {
        // code as before, but periodically call
        bytesDownloaded.set(...);
        // ...
    }

}

Now you do

@Override
protected Void call() throws Exception {

    FileDownloader downloader = new FileDownloader();

    downloader.bytesDownloadedProperty().addListener((obs, oldValue, newValue) -> 
        updateProgress(newValue.longValue(), downloader.getTotalBytes()));

    boolean isDownloaded = downloader.downloadSysVersion();

    // ...
}

If you want to make the FileDownloader class independent of the entire JavaFX API (including the property classes), you can use a LongConsumer instead:

public class FileDownloader {

    private LongConsumer progressUpdate ;

    public void setProgressUpdate(LongConsumer progressUpdate) {
        this.progressUpdate = progressUpdate ;
    }

    private long totalBytes ;

    public long getTotalBytes() {
        return totalBytes ;
    }

    public boolean downloadSysVersion() {
        // periodically do
        if (progressUpdate != null) {
            progressUpdate.accept(...); // pass in number of bytes downloaded
        }
        // ...
    }
}

and in this case your task looks like

@Override
protected Void call() throws Exception {

    FileDownloader downloader = new FileDownloader();

    downloader.setProgressUpdate(bytesDownloaded -> 
        updateProgress(bytesDownloaded, downloader.getTotalBytes()));

    boolean isDownloaded = downloader.downloadSysVersion();

    // ...
}

With either of these setups, you can then just bind the progress bar's progressProperty to the task's progressProperty as usual.

Saturday, October 23, 2021
 
ar.gorgin
answered 1 Month ago
86

You have to do your database stuff into an other thread, cause if the operation take time it will freez the JavaFX thread (The GUI)

In JavaFx you can use Service and Task to do background stuff. You should read

  • Concurency in javafx
  • Service

By executing your database stuff into a service, you will be able to monitor it easely cause Service provide the necessary to do that, and have method like onSuccedeed, onFailed...

Really have a look to that, is an essential part if you want to do JavaFx correctly.

Main.java

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        primaryStage.setTitle("Hello World");
        primaryStage.setScene(new Scene(root, 300, 275));
        primaryStage.show();
    }

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

ServiceExample.java

import javafx.concurrent.Service;
import javafx.concurrent.Task;

public class ServiceExample extends Service<String> {
    @Override
    protected Task<String> createTask() {
        return new Task<String>() {
            @Override
            protected String call() throws Exception {
                //DO YOU HARD STUFF HERE
                String res = "toto";
                Thread.sleep(5000);
                return res;
            }
        };
    }
}

Controller.java

import javafx.concurrent.WorkerStateEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.control.ProgressIndicator;

public class Controller {

    @FXML
    private ProgressIndicator progressIndicator;

    public void initialize() {
        final ServiceExample serviceExample = new ServiceExample();

        //Here you tell your progress indicator is visible only when the service is runing
        progressIndicator.visibleProperty().bind(serviceExample.runningProperty());
        serviceExample.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
            @Override
            public void handle(WorkerStateEvent workerStateEvent) {
                String result = serviceExample.getValue();   //here you get the return value of your service
            }
        });

        serviceExample.setOnFailed(new EventHandler<WorkerStateEvent>() {
            @Override
            public void handle(WorkerStateEvent workerStateEvent) {
                //DO stuff on failed
            }
        });
        serviceExample.restart(); //here you start your service
    }
}

sample.fxml

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

<?import javafx.scene.control.ProgressIndicator?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="200.0" prefWidth="200.0" xmlns:fx="http://javafx.com/fxml/1"
            xmlns="http://javafx.com/javafx/2.2" fx:controller="Controller">
    <ProgressIndicator fx:id="progressIndicator" layoutX="78.0" layoutY="55.0"/>
</AnchorPane>

I do that example quickly it's basic but I think it's what you want. (I don't add my progressIndicator to a node it's just for the example)

Saturday, November 6, 2021
 
Cavyn VonDeylen
answered 3 Weeks ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share