Java FX, предотвращающий зависание пользовательского интерфейса - PullRequest
0 голосов
/ 24 января 2020

Я хочу, чтобы пользователь дождался завершения вызова моего метода для загрузки файла, что может занять от 2 до 3 минут. Я создал новый поток для вызова метода и вызвал его внутри run() метода. На основе возвращаемого значения должно отображаться сообщение, успешное или ошибочное. Следовательно, я должен был использовать join(). Но это все еще зависает, так как пользовательский интерфейс все еще ожидает завершения вызова метода. Как я могу преодолеть эту проблему? (По сути, мне нужно использовать поток для пользовательского интерфейса, чтобы не зависать, и в то же время я хочу отобразить сообщение на основе возвращаемого значения метода)

btn.setOnAction(new EventHandler<ActionEvent>() {
   @Override
   public void handle(ActionEvent event) {
      processMsg.setText("Do not close. This may take few minutes...");
      Thread t1 = new Thread(new Runnable(){
         @Override
         public void run() {
            try {
                   ret = /*Method-call-which-takes-long-time*/
                } catch (IOException ex) {
                     Logger.getLogger(JavaFXApplication1.class.getName()).log(Level.SEVERE, null, ex);
                } catch (SQLException ex) {
                     Logger.getLogger(JavaFXApplication1.class.getName()).log(Level.SEVERE, null, ex);
                }
         }
      });
      t1.start();
      try {
           t1.join();
      } catch (InterruptedException ex) {
           Logger.getLogger(JavaFXApplication1.class.getName()).log(Level.SEVERE, null, ex);
      }
      if (ret != null){
         if (ret.equals("TRUE")){
            pane.getChildren().remove(processMsg);
            processMsg.setText("File downloaded at location: "+ chosenDir.getAbsolutePath());
            pane.add(processMsg,0,11,2,1);
         }
         else{
            pane.getChildren().remove(processMsg);
            processMsg.setText(ret);
            pane.add(processMsg,0,11,2,1);
         }
      }
   }
});

Ответы [ 3 ]

5 голосов
/ 24 января 2020

На основе возвращаемого значения должно отображаться сообщение, успешное или ошибочное. Следовательно, мне пришлось использовать join().

Это предположение неверно. Есть способы обновить GUI из фонового потока. Platform.runLater является наиболее гибким, но в этом случае я просто рекомендую использовать Task, что позволяет добавлять обработчики через свойства onSucceeded и onFailed, которые Вызывается, когда call logi c завершается с или без создания исключения:

Task<MyReturnType> task = new Task<MyReturnType>() {

    @Override
    protected MyReturnType call() throws Exception {
        return someLongCalculation(); // may throw an exception
    }

};

task.setOnSucceeded(evt -> {
    // we're on the JavaFX application thread here
    MyReturnType result = task.getValue();
    label.setText(result.toString());

});

task.setOnFailed(evt -> {
    // we're on the JavaFX application thread here
    label.setText("Error: " + task.getException().getMessage());
});
new Thread(task).start(); // alternatively use ExecutorService

Task.updateValue, Task.updateMessage и Task.updateProgress позволяют передавать частичные результаты обратно, которые можно наблюдать с помощью свойства value, message и progress; эти свойства обновляются в потоке приложения JavaFX.

2 голосов
/ 24 января 2020

Я советую вам проверить это Oracle Учебное пособие о том, как обрабатывать бизнес-логи c вне компонента пользовательского интерфейса.

Кроме того, в отношении вашего вопроса использование CompletableFuture будет иметь большая помощь для выполнения такой асинхронной работы.

Если быть точным c, whenComplete() поможет, если вы используете процесс загрузки в CompletableFuture, а затем отобразите сообщение.

Здесь является ссылкой для whenComplete ()

0 голосов
/ 24 января 2020

Возможно, вы знаете, что рендеринг JavaFX выполняется потоком с именем Поток приложений JavaFX. Вы можете видеть потоки в режиме Eclipse Debug. Таким образом, если этот поток будет ждать другого, рендеринг остановится. Вот что здесь происходит.

Что вы можете делать

  1. Когда эта кнопка нажата, отключите кнопки, которые пользователь не хочет нажимать.
  2. Используйте контейнер для опроса результатов. Это может быть глобальная очередь или элегантно реализованная с использованием Rx Java BehaviorSubject или какая-либо другая подходящая реализация Subject.
  3. Когда метод, отнимающий много времени, завершится, он заполняет очередь результатом или вызовите onNext() на Rx Java Subject.
  4. Когда слушатель очереди или Subject получает значение, он снова включает кнопки.

Пусть я посмотрю, смогу ли я быстро собрать образец. А пока вы можете попробовать сами.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...