23

I have a simple JavaFX 2 app, with 2 buttons, saying Start and Stop. When the start button is clicked, I want to create a background thread which will do some processing and update the UI (e.g a progress bar) as it goes along. If the stop button is clicked, I want the thread to terminate.

I've tried to do this using the javafx.concurrent.Task class which I gathered from the documentation would work fine for this. But whenever I click Start, the UI freezes/hangs rather than staying normal.

Her's the code from the main Myprogram extends Application class for showing the buttons:

public void start(Stage primaryStage)
{               
    final Button btn = new Button();
    btn.setText("Begin");

    //This is the thread, extending javafx.concurrent.Task :
    final MyProcessor handler = new MyProcessor();
    btn.setOnAction(new EventHandler<ActionEvent>()
    {
        public void handle(ActionEvent event)
        {                
           handler.run(); 
        }
    });

    Button stop = new Button();
    stop.setText("Stop");
    stop.setOnAction(new EventHandler<ActionEvent>()
        {
             public void handle(ActionEvent event)
             {
                handler.cancel();
             }
        }

    );
    // Code for adding the UI controls to the stage here.
}

Here's the code of MyProcessor class:

import javafx.concurrent.Task;
public class MyProcessor extends Task
{   
    @Override
    protected Integer call()
    {
        int i = 0;
        for (String symbol : feed.getSymbols() )
        {
            if ( isCancelled() )
            {
                Logger.log("Stopping!");
                return i;
            }
            i++;
            Logger.log("Doing # " + i);
            //Processing code here which takes 2-3 seconds per iteration to execute
            Logger.log("# " + i + ", DONE! ");            
        }
        return i;
    }
}

Pretty simple, but the UI hangs whenever I click the Start button, though the console messages continue to get displayed (Logger.log simply does System.out.println )

What am I doing wrong?

Ali
  • 261,656
  • 265
  • 575
  • 769

1 Answers1

25

Task implements Runnable, so when you call handler.run(); you actually run the call method in the UI Thread. That will hang the UI.

You should start the task in a background thread, either via an executor or simply by calling new Thread(handler).start();.

This is explained (maybe not very clearly) in the javadoc or in the JavaFX concurrency tutorial.

assylias
  • 321,522
  • 82
  • 660
  • 783
  • I have one more question. I want to update a label in the main UI with the progress of the task thread. For this I would use this code in the task: `mainProg.updateStatus(i);`, and in main program: `public void updateStatus(int i) { this.label.setText(i) ); }`. Would this work, or should I use `platform.runLater()` from `MainProg.updateStatus()` for updating the UI? – Ali Feb 03 '13 at 17:58
  • 1
    Yes you would need to run it in the UI Thread with runLater - Alternatively, you can use the updateProgress method (see example 3 in the tutorial) which will run in the UI thread. – assylias Feb 03 '13 at 18:16
  • For those who reach this topic searching an answer how to bind backend tasks and UI so that backend updates UI, I recommend this article: http://www.oracle.com/technetwork/articles/java/enterprisefx-1651840.html . It somehow explains topic littlebit better than "JavaFX concurrency tutorial", which comes up with every Google search. – ilvez Feb 23 '13 at 08:53
  • @TaaviIlves how is your link relevant? "Learn to use LightView to convert REST services into a bindable set of properties". – likejudo Feb 28 '13 at 16:49
  • Only so that it explains communication with backend and frontend with working example more verbose. For specific question & answer it's not that relevant indeed. I wasn't misleading on purpose, but lots of googling always brought me to "JavaFX concurrency tutorial" that didn't help me much firsthand. – ilvez Mar 01 '13 at 07:15