简体   繁体   English

JavaFX-从GUI接收事件后返回主线程

[英]JavaFX - coming back to main thread after receiving event from gui

I am writing a JavaFX application and realized that too many things happen on FX thread. 我正在编写JavaFX应用程序,并且意识到FX线程上发生了太多事情。 One of the root causes is that gui events, like button click, spawn backend actions on FX thread. 根本原因之一是gui事件(如按钮单击)在FX线程上产生了后端操作。 Actually just after receiving the event, we're on FX thread, so any further calls stay on it. 实际上,刚收到事件后,我们就进入了FX线程,因此任何其他调用都将保留在该线程上。 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? 有什么方法可以简单地返回到MAIN应用程序线程,然后在需要时返回Platform.runLater,或者我必须使用例如RX或某些执行程序服务来处理它?

Thanks 谢谢

The way out of the Event Loop is very simple - it's just Java. 退出事件循环的方法非常简单-只是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 例如,要在“后台”完成某项操作然后更新GUI,您可以执行以下操作

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.). 我通常希望将main线程分配给事件循环,并使用自定义执行程序进行后台工作(因为后台工作是特定于应用程序的,因此可能需要更多线程等)。


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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM