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();
}
}