Reputation: 589
I am writing a JavaFX application and realized that too many things happen on FX thread. One of the root causes is that gui events, like button click, spawn backend actions on FX thread. Actually just after receiving the event, we're on FX thread, so any further calls stay on it. Is there any way to simply get back to MAIN application thread, and then Platform.runLater when is needed, or I have to deal with it using for instance RX or some executor services?
Thanks
Upvotes: 1
Views: 677
Reputation: 18864
The way out of the Event Loop is very simple - it's just Java. Use anything you would use normally — executors, queues, etc.
For example, to get something done "in the background" and then update the GUI, you would do something like
final Executor backgroundWorker = Executors.newSingleThreadExecutor();
...
backgroundWorker.execute(()-> // from the EventLoop into the Worker
{
val result = doThatLongRunningTask();
Platform.runLater(() -> // back from the Worker to the Event Loop
{
updateUiWithResultOfLongRunningTask(result);
}
});
I would normally expect the main
thread to be given to the Event Loop and use a custom executor for background work (because the background work is application-specific, so may need more threads, etc.).
If for whatever exotic reason (there are really none I can think of) you want it the other way around:
So, to use the main thread as an executor all we need is:
public final class MyApp extends Application {
private static final Logger LOG = LoggerFactory.getLogger(MyApp.class);
private static final Runnable POISON_PILL = () -> {};
private final BlockingQueue<Runnable> tasks = new LinkedBlockingQueue<>();
private final Executor backgroundWorker = this::execute;
private final Future<Void> shutdownFuture = new CompletableFuture<>();
private final Executor eventLoop = Executors.newSingleThreadExecutor();
/** Get background worker */
public Executor getBackgroundWorker() {
return backgroundWorker;
}
/** Request backgroun worker shutdown */
public Future shutdownBackgroundWorker() {
execute(POISON_PILL);
return shutdownFuture;
}
private void execute(Runnable task) {
tasks.put(task);
}
private void runWorkerLoop() throws Throwable {
Runnable task;
while ((task = tasks.take()) != POISON_PILL) {
task.run();
}
shutdownFuture.complete(null);
}
public static void main (String... args) throws Throwable {
final MyApp myApp = new MyApp(args);
LOG.info("starting JavaFX (background) ...");
eventLoop.execute(myApp::launch);
LOG.info("scheduling a ping task into the background worker...");
myApp.runLater(() -> {
LOG.info("#1 we begin in the event loop");
myApp.getBackgroundWorker().execute(() -> {
LOG.info("#2 then jump over to the background worker");
myApp.runLater(() -> {
LOG.info("#3 and then back to the event loop");
});
});
});
LOG.info("running the backgound worker (in the foreground)...");
myApp.runWorkerLoop();
}
}
Upvotes: 4