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
The example doesn't work at all. I'm using eclipse and everythere are erros!
Posted by: Bruce | October 13, 2009 at 10:10 AM
Hi Jim,
I have read your artical which is pretty much informative.
with a reference of your example i have create one sample application which asynchronously call the webservices.
instread of ticker class ( in your example) i have implement RunnableFuture interface to a class [Control.java] which is responsible for making SOAP based webservices calls.
and i have override the run method in which i am making webservice request and getting back the response in a TickerHandler.
so my main.fx calls taskController.start() to start the thread.
But unfortunately when i click on a button to call webservices it hangs for a which then a response is return.
so eventually i am failed to create asynchronous web service request..
so please do help me how i would achieve this so that my other javafx component wont hang....
Nihar (ntimesc@gmail.com)
Posted by: Nihar | September 05, 2009 at 07:43 AM
Hi Jim ,
I have read your article regarding Asynchronicty in JavaFX. it is really a helpful and informative article.
As of now i am working on a application which extensively making webservices calls to perform ongoing computation i have chose javafx as a user interface platform. after reading your article i have tried to create new thread as and when a new webservice request is made.
i have implemented RunnableFuture in the class which is making webservices request.
so does this approach work fine for ongoing computational webservices with javafx ui ?
Posted by: Nihar | September 05, 2009 at 05:36 AM
I copied and pasted the example code shown into a NetBeans Project, FX 1.2 (Linux). It won't compile. And the block seems to happen at the mixin Inheritance feature, which I suspect, is related to the interface. TaskController insists that only one non-mixin FX class can be extended. FX doesn't like the interface at all. The parsing keeps insisting we left out a semicolon on the interface.
Forgive me for asking this here. But I did buy the first Apress FX book, got excited, then Java dumps Linux. Now Linux is back on board with 1.2 and I'm trying to learn JavaFX vers. 1.2. Each FX version had profound vocubulary changes that requires re-engineering the examples I do find on the web.
Posted by: VictorCharlie | July 03, 2009 at 04:37 PM