Приложение зависает или «Не в потоке приложения FX» происходит во время активности приложения

#java #javafx

#java #javafx

Вопрос:

Приложение реагирует на действия, которые происходят на геймпаде. При нажатии кнопки что-то происходит в пользовательском интерфейсе. Но я столкнулся с проблемой зависания приложения или «java.lang.Исключение IllegalStateException: не в потоке приложения FX».

Чтобы исправить это, я попробовал следующие подходы: Platform.runLater() и Task использование. Но это не помогло.

Вот код проблемы:

 public class GamepadUI extends Application{

    private static final int WIDTH = 300;
    private static final int HEIGHT = 213;

    private Pane root = new Pane();
    private ImageView iv1 = new ImageView();

    private boolean isXPressed = false;

    @Override
    public void start(Stage stage) throws Exception {
        initGUI(root);

        Scene scene = new Scene(root, WIDTH, HEIGHT);
        stage.setScene(scene);
        stage.setResizable(false);
        stage.show();
    }

    public void pressBtn() {
        if(!isXPressed) {
            iv1.setVisible(true);
            isXPressed = true;
        }
    }

    public void releaseBtn() {
        if(isXPressed) {
            iv1.setVisible(false);
            isXPressed = false;
        }
    }

    private void initGUI(final Pane root) {
        Image image = new Image(Props.BUTTON);
        iv1.setImage(image);
        iv1.setLayoutX(198);
        iv1.setLayoutY(48);
        iv1.setVisible(false);
        root.getChildren().add(iv1);

        runTask();
    }

    public void runTask() {
        Task task = new Task<Void>() {
            @Override
            protected Void call() throws Exception {
                initStubGamepad();
                return null;
            }
        };

        new Thread(task).start();
    }

    public static void main(String[] args) {
        launch(args);
    }

    public void initStubGamepad() {
        Random rnd = new Random();
        try {
            while (true) {
                if (rnd.nextInt(30) == 3) {
                    pressBtn();
                } else if (rnd.nextInt(30) == 7) {
                    releaseBtn();
                }
            }
        } catch (Exception ex) {
            System.out.println("Exception: "   ex);
        }
    }
}
 

initStubGamepad() эмулирует опрос активности кнопок геймпада. Когда пользователь нажимает любую кнопку ( rnd.nextInt(30) == 3 ) — в пользовательском интерфейсе появляется изображение. Когда пользователь отпускает эту кнопку ( rnd.nextInt(30) == 7 ) — изображение исчезает из пользовательского интерфейса.

В случае java.lang.IllegalStateException: Not on FX application thread , если происходит выше. Если вы измените RunTask () на что-то вроде этого:

 Platform.runLater(new Runnable() {
    @Override
    public void run() {
        initStubGamepad();
    }
});
 

Тогда приложение зависнет или даже основной пользовательский интерфейс вообще не появится, но активность геймпада продолжается.

Я хочу просто показывать / скрывать разные изображения при обнаружении какой-либо активности на геймпаде (кстати, нет способа отслеживать активность геймпада, кроме опроса геймпада в бесконечном цикле). Что я сделал не так

Ответ №1:

Объяснение

В первом сценарии, когда вы используете

 Task task = new Task<Void>() {
  @Override
  protected Void call() throws Exception {
      initStubGamepad();
      return null;
  }
}
 

Внутри initStubGamepad() , который выполняется для задачи, вы пытаетесь обновить компоненты пользовательского интерфейса внутри pressBtn() и releaseBtn() методы, поэтому вы сталкиваетесь с

 java.lang.IllegalStateException: Not on FX application thread
 

поскольку все обновления пользовательского интерфейса должны выполняться в потоке JavaFX

Во втором сценарии, когда вы используете

 Platform.runLater(new Runnable() {
    @Override
    public void run() {
        initStubGamepad();
    }
});
 

пользовательский интерфейс не отображается, потому что у вас есть бесконечный цикл внутри initStubGamepad() , который запускает поток приложения JavaFX в бесконечном цикле

Решение

К тому времени, когда вы достигнете здесь, вы, должно быть, уже нашли решение. В случае, если вы этого не сделали, попробуйте установить обновление компонентов Javafx в потоке пользовательского интерфейса. Итак, вместо вызова initStubGamepad() внутри Platform.runLater , попробуйте вызвать pressBtn() и releaseBtn() внутри него.

Попробуйте использовать

 while (true) {
   if (rnd.nextInt(30) == 3) {
      Platform.runLater(() -> pressBtn());            
   } else if (rnd.nextInt(30) == 7) {
       Platform.runLater(() -> releaseBtn());            
   }
}
 

или вы также можете использовать

 public void pressBtn() {
    if(!isXPressed) {
        Platform.runLater(() -> iv1.setVisible(true));
        isXPressed = true;
    }
}
 

Комментарии:

1. Спасибо, большое спасибо за объяснение. Я понял, что было не так в моем использовании Platform.runLater() и Task использовании.