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
Nice.
JavaFX is catching up demand it seems.
I liked this review about javafx posted on TaranFX http://www.taranfx.com/blog/?p=1173
JavaFX is powerful! I never knew abt this!
Posted by: Saurabh | June 30, 2009 at 07:19 PM
JavaFX 1.2 has mixin inheritance. Quoting Weiqi Gao from excerpts of chapter 4 of our Pro JavaFX book:
"Understanding Mixin Classes
JavaFX Script supports a form of inheritance called mixin inheritance. A JavaFX Script class can optionally extend one JavaFX Script class and any number of JavaFX Script mixin classes...
A mixin class is a class whose primary purpose is to be extended by other classes and cannot be instantiated directly. Such a class is defined by using the mixin modifier before the class keyword. Like regular classes, mixin classes may contain instance variable and constant declarations, instance function definitions, init blocks, and postinit blocks. Here is an example:
mixin class Locatable {
var x: Number;
var y: Number;
function moveToOrigin() {
x = 0.0;
y = 0.0;
}
function setLocation(x: Number, y: Number) {
this.x = x;
this.y = y;
}
}
The mixin class Locatable will cause classes that extend it to have instance variables x and y and instance functions moveToOrigin()and setLocation().
Extending Classes
You extend JavaFX Script classes by including a super class and mixin list in a class definition. The super class and mixin list appear after the class name and consist of the extends keyword followed by a comma-separated list of class names. The list may contain an optional regular (non-mixin) class and any number of mixin classes. The regular class in the super class and mixin list is called a super class of the class. The mixin classes in the super class and mixin list are called parent mixins of the class. A class is called a subclass of its super class and a mixee of its parent mixins.
A class inherits its super class’s instance variables, instance constants, and instance functions to which the writer of the super class has allowed access. The smallest unit of access rights in JavaFX Script is the JavaFX Script source file. Therefore, if a subclass is in the same file as the super class, it inherits all of its instance variables and instance functions.
A mixee will inherit its parent mixin’s instance variables, instance constants, and instance functions to which the writer of the parent mixin has allowed access."
In this blog post example, the TaskController JavaFX class is extending one JavaFX class and one Java interface.
Hope that helps, John!
Jim Weaver
Posted by: James Weaver | June 24, 2009 at 06:15 PM
Hi Jim,
The example you show above is surprising to me because of the multiple inheritance. I thought that multiple inheritance had been removed from JavaFX SDK 1.2?
Posted by: John O'Conner | June 24, 2009 at 01:42 PM