JavaFX Async Task

Today someone asked how to use the javafx.async package. The javafx.async package is useful if you have some background work to do that you don't want to run on the main JavaFX thread. Perhaps you are doing some serious number crunching, or maybe some file IO and if you did this on the main thread, the user would be blocked from doing anything and features like animation would scream to a halt. To illustrate how to use the async package, here is a trivial implementation that merely spawns a task that sleeps 5 seconds. Right before the sleep starts and right after the sleep ends, a message is sent back to the JavaFX main routine and displayed on the screen.

First, you need to create a Java class that implements javafx.async.RunnableFuture. RunnableFuture can only be implemented in a Java class and will throw a runtime exception if you try to use it directly on a JavaFX class. The following shows a minimal RunnableFuture that merely sleeps for 5 seconds.

// JavaSleepTask.java
public class JavaSleepTask implements RunnableFuture {
    @Override
    public void run() throws Exception {
        Thread.sleep(5000);
    }
}

Next you need to create a JavaFX class that extends javafx.async.JavaTaskBase and implements the create() : RunnableFuture function. The create() function will instantiate the JavaSleepTask object we just created in Java. For example,

//SleepTask.fx
public class SleepTask extends JavaTaskBase {
    protected override function create() : RunnableFuture {
        new JavaSleepTask();
    }
}
 
  

Lastly, to use the SleepTask you need to create an instance of it in JavaFX and call its start() function:

//Main.fx
var sleepTask =  SleepTask { };

sleepTask.start();

Under the cover, this creates a separate Thread of execution from the main JavaFX thread and in turn invokes the run() method on the JavaSleepTask object.

Now, what if our JavaFX application needs to be updated periodically while the JavaSleepTask object is performing its work. Perhaps, the task needs to crunch a lot of numbers, and you need to periodically be updated with progress.

A way to do this is to create a Java interface that acts as a callback mechanism. Let's say the interface looks like FXListener.java:

//FXListener.java
public interface FXListener {
    public void callback(String msg);
}

To use this, we first modify the JavaSleepTask.java file. We add a private attribute, listener, a constructor to save the passed in listener, and implement a postMessage() method to actually do the callback on the FXListener.Notice in the postMessage() method, we had to use the JavaFX  com.sun.javafx.runtime.Entry.deferAction() static method. JavaSleepTask will not be running on the main JavaFX thread, so it is necessary to actually make the call on the FXListener call back by pushing it onto the main JavaFX thread, using deferAction(). If you do not do this you will most likely get unpredictable results and exceptions.

//JavaSleepTask.java
import java.util.Date;
import com.sun.javafx.runtime.Entry;
import javafx.async.RunnableFuture;
 public class JavaSleepTask implements RunnableFuture {
    FXListener listener;
    public JavaSleepTask(FXListener listener) {
        this.listener = listener;

    }

    @Override
    public void run() throws Exception {
        postMessage("Start Task @ "+ new Date());
        Thread.sleep(5000);
        postMessage("End Task @ "+ new Date());
    }

    public void postMessage(final String msg) {
        Entry.deferAction(new Runnable() {
            public void run() {
                listener.callback(msg);
            }
        });
    }
}

Next, we modify the JavaFX SleepTask to add a listener variable that is passed to the constructor of the JavaSleepTask object.

//SleepTask.fx
import javafx.async.JavaTaskBase;
import javafx.async.RunnableFuture;

public class SleepTask extends JavaTaskBase {
    public-init var listener: FXListener;

    protected override function create() : RunnableFuture {
        new JavaSleepTask(listener); 
    }
}

Lastly, we modify our JavaFX application that uses the SleepTask. When the callback is invoked, the message is saved in a local message variable that is later bound to a Text object.

// Main.fx
var message: String;
var textFill: Color = Color.BLACK;
var count = 0;
var colors = [ Color.BLUE, Color.RED];

var listener = FXListener {
     override function callback(msg: String) : Void {
         message = msg;
         textFill = colors[count mod 2];
         count++;
     }
 }

var sleepTask =  SleepTask {
    listener: listener
};

sleepTask.start();

To actually see the change, we create a Stage with a Text shape, that displays the latest message while alternating the color.

//Main.fx
Stage {
    title : "Async Task Example"
    scene: Scene {
        width: 500
        height: 200
        content: [
            Text {
                font : Font { size: 24 }
                fill: bind textFill
                layoutX: 10
                layoutY: 30
                content: bind message
            }
        ]
    }
}

When this is first run, you see the message right before the sleep.

Start Date

Then 5 seconds later the window is updated with the End message:

End Message

The full source for this example is here.

Comments:

see another example
http://jfxstudio.wordpress.com/2009/06/09/asynchronous-operations-in-javafx/
Example requests Google Path API in asynchronous mode.

Posted by surikov on June 08, 2009 at 09:34 PM EDT #

I wrote another blog about asyn in JavaFX 1.2 at
http://blogs.sun.com/baechul/entry/javafx_1_2_async
Hope this help someone.

Posted by Baechul on June 12, 2009 at 09:55 AM EDT #

Thanks a lot.
Really helped me.

Posted by Dessort on June 24, 2009 at 07:05 PM EDT #

What can I do if I want to act on JavaFX objects in the other thread?
It's a java class, so I cant import JavaFX objects.

The context is: a CustomNode which has to be loaded appart from the main Thread because of the heavy files it contains.

Any suggestions?

Posted by Myst on June 24, 2009 at 07:47 PM EDT #

What can I do if I want to act on JavaFX objects in the other thread?
It's a java class, so I cant import JavaFX objects.

The context is: a CustomNode which has to be loaded appart from the main Thread because of the heavy files it contains.

Any suggestions?

Posted by Myst on June 24, 2009 at 07:59 PM EDT #

I can't believe the architects of JavaFX can not provide a simple out of the box TimerTask for JavaFX. Do I really have to code my own 'void run()' method in Java to simply wait x number of milliseconds? All this work above seems to be overkill for the developer who simply wants to wait and then run some code.

Posted by James Moliere on October 06, 2009 at 08:57 AM EDT #

Awesome, very, very helpful article. Async in JavaFX seemed like such a bear, but now I realize it's much more manageable!

Posted by David Young on December 03, 2009 at 07:24 PM EST #

hi!!
this really helped me so much,
however, when i try to port this into my HTC,/(or in the netbean FxTouchPhone emulator)
the background task is just not working as the desktop did,
do you have any suggestion?

Posted by Ginkan on May 14, 2010 at 10:15 AM EDT #

Dear James Moliere,
I think you can use Timeline to periodically run some code.

Posted by Alan on May 16, 2010 at 10:02 PM EDT #

Hi, thanks for this very good article!!

I have already experience with silverlight combined with webservice calls. I'don't know what went wrong with javafx , but there seems to be something very very wrong.

I hope there will be an easy to use framework for asynchronous webservice calling in javafx like in silverlight.

I thought business applications are the strength of javafx, but ...

Posted by Claude Glauser on December 10, 2010 at 10:25 AM EST #

Post a Comment:
  • HTML Syntax: NOT allowed
About

jimclarke

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today