JavaFX show loading dialog for longer operations
Asked Answered
T

2

5

I got some operations in my Controller class which could take some time. So I want to show a loading dialog while this operation is running.

I tried this:

Platform.runLater(new Runnable() {
  @Override
  public void run() {
     loadingDialog.show();
  }
});

Boolean opSuccess = myService.operate();

Platform.runLater(new Runnable() {
   @Override
   public void run() {
     loadingDialog.hide();
   }
});

if (opSuccess) {
   // continue
}

Now, the Problem is, the loadingDialog is never show. The UI only blocks for some time and than continues on "//continue".

So it seems, the runLater call is blocked by the blocking operation (operate)?

I also tried CoundDownLatch, to wait for loadingDialog.show() to complete, before running myService.operate(). But the latch.await() method never completes.

So my question is, how my I show the loadingDialog until myService.operate() finished and returned true or false? Do I have to put the operate() call into another thread and run it async or is there an easier way?

Thanks for help.

Toper answered 30/4, 2015 at 9:8 Comment(1)
Provide an mcve.Spinet
K
16

Are you sure your entire code does not run in the JavaFX Thread? Methods of your controller class usually do and I assume it due to your description.

However, better use the Task class. Here you'll find a tutorial and a short snippet for your application:

// here runs the JavaFX thread
// Boolean as generic parameter since you want to return it
Task<Boolean> task = new Task<Boolean>() {
    @Override public Boolean call() {
        // do your operation in here
        return myService.operate();
    }
};

task.setOnRunning((e) -> loadingDialog.show());
task.setOnSucceeded((e) -> {
    loadingDialog.hide();
    Boolean returnValue = task.get();
    // process return value again in JavaFX thread
});
task.setOnFailed((e) -> {
  // eventual error handling by catching exceptions from task.get()  
});
new Thread(task).start();

I assumed Java 8 and the possibility to use Lambda expressions. Of course it is possible without them.

Kathlyn answered 30/4, 2015 at 10:32 Comment(3)
You're right. My code snippet above runs in FX Thread since it is the Controller. But I realized that I got strange ConcurrencyExceptions when trying to update UI components without Runlater. So, does it change something to the task solution you provided? I also tried the same code of my first example without Runlater. Simply with: loadingDialog.show(); Boolean success = myService.operate(); loadingDialog.hide(); But the result is the same. UI is blocked and no dialog was shown.Toper
You get these exceptions if you change UI outside the FX Thread, otherwise they should not occur. No, my solution must run in FX Thread, so it should work. Of course, as long as you run your expensive computation in FX Thread the UI blocks. With new Thread(task).start(); you now run it in a seperate thread and the callbacks are called when the work is done (again in FX Thread).Kathlyn
Yeah, thanks. This is working. It's a little bit more complicated since I want to modify and call UI components from operate(), but this is my problem. So I have to restructure the code in another way, separating UI calls from heavy background operations.Toper
I
1

You are better off making use of concurrency mechanisms/Worker interfaces in JavaFx - Tasks and services instead of using Platform.runLater(). The tasks and services allow you to manage the long running tasks in a separate thread. They also provide callbacks to indicate the progress of the tasks.

You could explore further at http://docs.oracle.com/javafx/2/threads/jfxpub-threads.htm

Also have a look at the Ensemble JavaFX samples for Tasks and Services - http://www.oracle.com/technetwork/java/javase/overview/javafx-samples-2158687.html

Indifference answered 30/4, 2015 at 10:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.