JavaFX - возвращение в основной поток после получения события от графического интерфейса - PullRequest
0 голосов
/ 21 сентября 2018

Я пишу приложение JavaFX и понял, что в FX-потоке происходит слишком много вещей.Одна из основных причин заключается в том, что события графического интерфейса, такие как нажатие кнопки, порождают внутренние действия в потоке FX.На самом деле, сразу после получения события мы находимся в потоке FX, поэтому любые дальнейшие звонки остаются на нем.Есть ли способ просто вернуться к главному потоку приложения, а затем к Platform.runLater, когда это необходимо, или мне приходится иметь дело с ним, используя, например, RX или некоторые службы executor?

Спасибо

1 Ответ

0 голосов
/ 21 сентября 2018

Выход из цикла обработки событий очень простой - это просто Java.Используйте все, что вы обычно используете - исполнители, очереди и т. Д.

Например, чтобы сделать что-то «в фоновом режиме», а затем обновить графический интерфейс, вы должны сделать что-то вроде

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

Как правило, я ожидаю, что поток main будет передан в цикл обработки событий и будет использовать пользовательский исполнитель для фоновой работы (поскольку фоновая работа зависит от приложения, поэтому может потребоваться больше потоков и т. Д.).


Если по какой-либо экзотической причине (на самом деле я не могу придумать ни одной) вы хотите это наоборот:

Итак, для использования основного потока в качестве исполнителя все, что нам нужно, это:

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();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...