The asynchronous task model has been much improved in JavaFX 1.2. Not only does it have a simple and elegant API, but several of the asynchronous tasks (e.g. HttpRequest) in the JavaFX API now use this model. In addition, there are a couple of progress UI controls introduced in JavaFX 1.2, and they work well with the new asynchronous model.
The JavaFX package that contains the asynchronous-related classes is javafx.async, and a good class to take a look at first is JavaTaskBase. This class enables you to start a task, check on its progress, and be notified when the task has completed. JavaFX is single threaded, so the task that is started is one that you define in a Java class (that implements the javafx.async.RunnableFuture interface). The Java class can then call functions of your JavaFX classes, demonstrating bi-directional integration between JavaFX and Java.
Take a look at the screenshot of a simple example on which Stephen Chin and I collaborated, that demonstrates the capabilities just described:
Clicking on the Start the Task button starts a new task and adds a ProgressBar into the scene (up to a maximum of eight, the built-in limit on the number of tasks in JavaFX that can be run in parallel). When a task completes, its ProgressBar is removed from the scene. Take a look at the AsyncProgressMain.fx script below to understand how the UI is drawn, and how the tasks are started:
package projavafx.asyncprogress.ui;
import projavafx.asyncprogress.model.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
var vbox:VBox;
function startTask() {
def progressBar:ProgressIndicator = ProgressBar {
progress: bind taskController.floatProgress
}
def taskController:TaskController = TaskController {
maxProg: 100
onStart:function():Void {
insert progressBar into vbox.content;
}
onDone:function():Void {
delete progressBar from vbox.content;
}
}
taskController.start();
}
Stage {
title: "Async and Progress Example"
scene: Scene {
width: 200
height: 250
content: vbox = VBox {
layoutX: 10
layoutY: 10
spacing: 10
content: [
Button {
text: "Start the task"
action: function():Void {
println("Starting TaskController");
startTask();
}
}
]
}
}
}
Now take a look at the TaskController class that is instantiated in the script above when the button is clicked:
package projavafx.asyncprogress.model;
import projavafx.asyncprogress.model.Ticker;
import javafx.async.RunnableFuture;
import javafx.async.JavaTaskBase;
public class TaskController extends JavaTaskBase, TickerHandler {
public var maxProg:Integer;
public-read protected var floatProgress:Number;
override public function create():RunnableFuture {
maxProgress = maxProg;
return new Ticker(this);
}
override public function onTick(tickNum:Integer):Void {
progress = tickNum;
floatProgress = percentDone / 100.0;
}
}
Note that the TaskController class above extends the JavaTaskBase class mentioned earlier. We'll use its capabilities to start and monitor the task. When the create function of the TaskController instance is called by the JavaFX runtime (as a result of its start function being called) it creates a new instance of the Ticker class that we developed in Java. As shown in the code below, the Ticker class implements the RunnableFuture interface mentioned earlier.
package projavafx.asyncprogress.model;
import com.sun.javafx.functions.Function0;
import javafx.lang.FX;
import javafx.async.RunnableFuture;
public class Ticker implements RunnableFuture {
TickerHandler tickerHandler;
public Ticker (TickerHandler tickerHandler) {
this.tickerHandler = tickerHandler;
}
@Override public void run() {
for (int i = 1; i <= 100; i++) {
if (tickerHandler != null) {
final int tick = i;
FX.deferAction(new Function0<Void>() {@Override public Void invoke() {
tickerHandler.onTick(tick);
return null;
}
});
}
try {
Thread.sleep(200);
}
catch (InterruptedException te) {}
}
System.out.println("Ticker#run is finished");
}
}
The Ticker class shown above counts to 100, sleeping for a couple hundred milliseconds each iteration. During each iteration it calls the onTick function of the TaskController class shown previously, which implements the TickerHandler interface. Here's the code for that interface:
package projavafx.asyncprogress.model;
public interface TickerHandler {
void onTick(int tickNum);
}
For an excellent explanation (including UML sequence diagrams) of asynchronous tasks in JavaFX, see Baechul's JavaFX 1.2 Async blog post. Also take a look at the Richard Bair and Jasper Potts fxexperience.com post on the subject.
For those interested in JavaFX Mobile, here's a short video clip of this example running on an HTC Diamond phone. You may want to mute the audio, as I didn't mute the microphone when creating this video. ;-)
As I mentioned in the dynamic Timeline values post, I'm trying to encourage you to compile and run the examples in this blog. However, please leave a comment if you'd like a Java Web Start link.
Regards,
Jim Weaver
Recent Comments