Platform.Runlater and Task in Javafx

Platform.runLater and Task in JavaFX

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

How does Platform.runLater() function?

A JavaFX application runs on the Application thread, which handles all the UI elements. This means that if you click Button A and clicking that button starts method A that takes 5 seconds to complete, and then one second after clicking that button, you try to click Button B which starts method B, method B won't start until method A finishes. Or possibly Button B won't even work until method A finishes, I'm a little fuzzy on the detail there.

A good way to stop your application from freezing is to use Threads. To fix the above scenario, clicking Button A will start method A that starts a new Thread. Then the Thread can take as long as it wants to complete without locking up the UI and preventing you from clicking Button B.

Now, say something in method A needed to be on the application thread, for example, it updated a UI component, like a Label or a TextField. Then inside your Thread in Method A you would need to put the part that affects the UI into a Platform.runLater(), so that it will run on the Application Thread with the rest of the UI.

What this means for your example is that you have two options.

1. Don't use threads at all, since you don't want the user to be interacting with the UI while the updates are happening anyway.

2. move c.updateData() out of the Platform.runLater() like this:

 Runnable task = new Runnable() {
@Override
public void run() {
c.updateData();
Platform.runLater(new Runnable() {
@Override
public void run() {
try {
infoLogTextArea.appendText(c.getErrorLog().toString());
plotLabel.setText(c.getCityData().size()+" cities found and updated from internet");
infoLogTextArea.appendText("Successfully updated the data from Internet\n");
}catch (IOException e) {
infoLogTextArea.setText("Couldnot update the data from web: "+e.getMessage()+"\n");
}
finally {
plotButton.setDisable(false);
updateButton.setDisable(false);
}
}
});
}
};

Either one of those will work, but what you're doing right now is you're on the application thread, and then you start another thread whose only purpose is to run something on the application thread.

Java FX Platform.runLater(() - equivalent for long running Tasks

Just new Thread(() -> dosomething()).start() should do the trick

Calling Platform.runLater for multiple tasks in javaFX application

You could just add a onSucceded handler to the tasks. This handler is run on the application thread after successful completion of the tasks:

public void runEverything() {
...

EventHandler<WorkerStateEvent> handler = new EventHandler<WorkerStateEvent>() {
private int remainingTasks = tasks.size();

@Override
public void handle(WorkerStateEvent event) {
if ((--remainingTasks) == 0) {
try {
Parent parent = FXMLLoader.load(getClass().getResource("AolRun.fxml"));
SmartCC.fxmlval.add("AOLScreen.fxml");
SmartCC.stageval.add((Stage) run.getScne().getWindow());
Stage stage = new Stage();
stage.initStyle(StageStyle.UTILITY);
stage = (Stage) run.getScene().getWindow();
Scene scene = new Scene(parent);
stage.setScene(scene);
scene.getStylesheets().add("/css/Style.css");
stage.setTitle("AOL Output ");
stage.setResizable(false);
stage.show();
} catch (IOException ex) {
Logger.getLogger(AOLScreenController.class.getName()).log(Level.SEVERE, null, ex);
}
}
}

};

ExecutorService threadPool = Executors.newFixedThreadPool(5);
for (Task t : tasks) {
t.setOnSuceeded(handler);
threadPool.submit(t);
}
threadPool.shutdown();
}

UI Freezes Even with Task & Platform.runLater (Full Example)

This is happening because you are asking the UI thread to do a big bunch of things in one big lump. There is no way for the UI thread to exit the while loop until all 5000 nodes are created and added to the scene.

private void addNodesToUI(VBox mainBox) {
final int[] i = {0};

Accordion temp = new Accordion();

Platform.runLater(() -> {
mainBox.getChildren().add(temp);
});

while (i[0] < 5000) {
TitledPane tp = new TitledPane();
tp.setPrefWidth(300);
tp.setPrefHeight(12);
tp.setPadding(new Insets(10));
tp.setStyle("-fx-background-color: red;");

i[0]++;

Platform.runLater(() -> {
temp.getPanes().add(tp);
});
}
}

This will allow your nodes to be created in small batches. This way, the UI thread can attempt to render the UI while the nodes are added progressively.

For your FXML case, you can create and load the FXML in another thread. You only need to be in UI thread when you attach a scene branch into the scene. However, I would suspect that would only mitigate the effects, as you are still going to attach a big chunk at one go.

JavaFX 2: background and Platform.runLater vs Task/Service

The Task and Service classes are designed to encourage good practice and proper use of concurrency for some (but not all) common scenarios in GUI programming.

A typical scenario is that the application needs to execute some logic in response to a user action which may take a long time (maybe a long calculation, or, more commonly, a database lookup). The process will return a result which is then used to update the UI. As you know, the long-running process needs to be executed on a background thread to keep the UI responsive, and the update to the UI must be executed on the FX Application Thread.

The Task class provides an abstraction for this kind of functionality, and represents a "one-off" task that is executed and produces a result. The call() method will be executed on the background thread, and is designed to return the result of the process, and there are event listeners for when the task completes that are notified on the FX Application thread. The developer is strongly encouraged to initialize the Task implementation with immutable state and have the call() method return an immutable object, which guarantees proper synchronization between the background thread and the FX Application Thread.

There are additional common requirements on these kinds of tasks, such as updating a message or the progress as the task progresses. The application may also need to monitor the life-cycle state of the class (waiting to run, running, completed, failed with an exception, etc). Programming this correctly is quite subtly difficult, as it necessarily involves accessing mutable state in two different threads, and there are many application developers who are unaware of the subtleties. The Task class provides simple hooks for this kind of functionality and takes care of all the synchronization.

To use this functionality, just create a Task whose call() method returns the result of your computation, register a handler for when the state transitions from RUNNING to SUCCEEDED, and run the task in a background thread:

final Task<MyDataType> task = new Task<MyDataType>() {
@Override
public MyDataType call() throws Exception {
// do work here...
return result ;
}
};

task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent event) {
MyDataType result = task.getValue(); // result of computation
// update UI with result
}
});

Thread t = new Thread(task);
t.setDaemon(true); // thread will not prevent application shutdown
t.start();

The way this works behind the scenes is that the Task maintains a state property, which is implemented using a regular JavaFX ObjectProperty. The Task itself is wrapped in a private implementation of Callable, and the Callable implementation is the object passed to the superclass constructor. Consequently, the Callable's call() method is actually the method executed in the background thread. The Callable's call() method is implemented as follows:

  1. Schedule a call on the FX Application thread (i.e. using Platform.runLater()) that updates the state, first to SCHEDULED, then to RUNNING
  2. Invoke the call() method of the Task (i.e. the user-developed call() method)
  3. Schedule a call on the FX Application Thread that updates the value property to the result of the call() method
  4. Schedule a call on the FX Application Thread that updates the state property to SUCCEEDED

This last step will of course invoke listeners registered with the state property, and since the state change was invoked on the FX Application Thread, so to will those listeners' handle() methods.

For a full understanding of how this works, see the source code.

Commonly, the application may want to execute these tasks multiple discrete times, and monitor the current state representing all of the processes (i.e. "running" now means one instance is running, etc). The Service class simply provides a wrapper for this via a createTask() method. When the Service is started, it gets a Task instance by calling createTask(), executes it via its Executor, and transitions its own state accordingly.

There are of course many concurrency use cases that don't fit (at least cleanly) into the Task or Service implementations. If you have a single background Thread that is running for the entire duration of your application (so it represents a continuous process, rather than a one-off task), then the Task class is not a good fit. Examples of this might include a game loop, or (perhaps) polling. In these cases you may well be better off using your own Thread with Platform.runLater() to update the UI, but of course you have to handle proper synchronization of any variables that may be accessed by both threads. In my experience, it is worth spending some time thinking about whether these requirements can be re-organized into something that does fit into the Task or Service model, as if this can be done the resulting code structure is often much cleaner and easier to manage. There are certainly cases where this is not the case, however, in which case using a Thread and Platform.runLater() is appropriate.

One last comment on polling (or any other requirement for a periodically-scheduled background task). The Service class looks like a good candidate for this, but it turns out to be quite hard to manage the periodicity effectively. JavaFX 8 introduced a ScheduledService class which takes care of this functionality quite nicely, and also adds handling for cases such as repeated failure of the background task.

Java Timer - Updating Labels with Platform.runLater

The Runnable you pass to Platform#runLater(Runnable) contains an infinite loop. That means you execute an infinite loop on the JavaFX Application Thread which is why your UI becomes unresponsive. If the FX thread is not free to do its job then no user-generated events can be processed and render "pulses" cannot be scheduled. That latter point is why the UI does not update despite you calling setText(...) continuously.

The fix, if you want to continue your current approach, is to remove the for (;;) loop from your Runnable implementation. You setup the TimerTask to be executed once every millisecond which means all you have to do is calculate the new state and set the labels once per execution. In other words, the run() method is already "looped". For example:

TimerTask task = new TimerTask() {
@Override public void run() {
Platform.runLater(() -> {
// calculate new state...

// update labels...

// return (no loop!)
});
}
};

That said, there's no reason to use a background thread for this. I recommend using the animation API provided by JavaFX instead. It's asynchronous but executed on the FX thread, making it simpler to implement and reason about—using multiple threads is always more complicated. To do something similar to what you're currently doing you can use a Timeline or PauseTransition in place of the java.util.Timer. The JavaFX periodic background task Q&A gives some good examples of using animations for this purpose.

Personally, I would use an AnimationTimer to implement a stopwatch. Here's an example:

import java.util.concurrent.TimeUnit;
import javafx.animation.AnimationTimer;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.ReadOnlyLongProperty;
import javafx.beans.property.ReadOnlyLongWrapper;

public class Stopwatch {

private static long toMillis(long nanos) {
return TimeUnit.NANOSECONDS.toMillis(nanos);
}

// value is in milliseconds
private final ReadOnlyLongWrapper elapsedTime = new ReadOnlyLongWrapper(this, "elapsedTime");
private void setElapsedTime(long elapsedTime) { this.elapsedTime.set(elapsedTime); }
public final long getElapsedTime() { return elapsedTime.get(); }
public final ReadOnlyLongProperty elapsedTimeProperty() { return elapsedTime.getReadOnlyProperty(); }

private final ReadOnlyBooleanWrapper running = new ReadOnlyBooleanWrapper(this, "running");
private void setRunning(boolean running) { this.running.set(running); }
public final boolean isRunning() { return running.get(); }
public final ReadOnlyBooleanProperty runningProperty() { return running.getReadOnlyProperty(); }

private final Timer timer = new Timer();

public void start() {
if (!isRunning()) {
timer.start();
setRunning(true);
}
}

public void stop() {
if (isRunning()) {
timer.pause();
setRunning(false);
}
}

public void reset() {
timer.stopAndReset();
setElapsedTime(0);
setRunning(false);
}

private class Timer extends AnimationTimer {

private long originTime = Long.MIN_VALUE;
private long pauseTime = Long.MIN_VALUE;
private boolean pausing;

@Override
public void handle(long now) {
if (pausing) {
pauseTime = toMillis(now);
pausing = false;
stop();
} else {
if (originTime == Long.MIN_VALUE) {
originTime = toMillis(now);
} else if (pauseTime != Long.MIN_VALUE) {
originTime += toMillis(now) - pauseTime;
pauseTime = Long.MIN_VALUE;
}

setElapsedTime(toMillis(now) - originTime);
}
}

@Override
public void start() {
pausing = false;
super.start();
}

void pause() {
if (originTime != Long.MIN_VALUE) {
pausing = true;
} else {
stop();
}
}

void stopAndReset() {
stop();
originTime = Long.MIN_VALUE;
pauseTime = Long.MIN_VALUE;
pausing = false;
}
}
}

Warning: While the AnimationTimer is running the Stopwatch instance cannot be garbage collected.

The above exposes a property, elapsedTime, which represents the elapsed time in milliseconds. From that value you can calculate the amount of days, hours, minutes, seconds, and milliseconds that have passed since you started the stopwatch. You simply have to listen to the property and update the UI when the property changes.



Related Topics



Leave a reply



Submit