Как остановить "JavaFX Application Thread"

У меня есть простое консольное приложение, которое иногда нужно выполнять графические операции, для тех, у кого я использую фреймворк JavaFx (есть некоторые функции, которые мне нужны, как стиль CSS для текста) Я просто генерирую некоторые фигуры и текст в скрытую сцену, а затем сохраняю их в файле и все,

Я знаю, что для работы с JavaFx мне нужно передать графические операции в поток JavaFx, но когда все будет готово, и я должен закрыть приложение (через несколько часов), этот поток JavaFx по-прежнему остается открытым... и я действительно не хотите принудительно выходить с System.exit(), потому что если что-то заблокировано, я могу знать/ждать (ТАКЖЕ, я не хочу выполнять все как приложение JavaFx (поскольку компоненты JavaFx составляют менее 1% от моего основное приложение)

код очень прост и googling вокруг Я нашел только использовать

Platform.exit();

который, похоже, не работает, я также пробовал играть с такими параметрами платформы, как

Platform.setImplicitExit(false);

вот мое тестовое приложение, которое вы можете запустить:

import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.geometry.Pos;
import javafx.scene.layout.VBox;

public class SOTestFX {
    public static void main(String[] args) {
        SOTestFX t = new SOTestFX();
        t.runFxThread();
    }

    public  void runFxThread(){
        //Application.launch(args);
        final JFXPanel jfxPanel = new JFXPanel(); 
        Platform.runLater(new Runnable() {
            @Override
            public void run() {

                System.err.println("CREATING IMAGE");
                simpleFXoperations();
                System.err.println("NOW CALL EXIT");
                System.err.println("JAVA FX THREAD SHOULD BE EXITED NOW");
                Platform.exit();
            }
        });

        try {
            Thread.sleep(3000); // just wait a bit if something should happen, let it happen..
        } catch (InterruptedException e) {
            e.printStackTrace();  
        }
        //jfxPanel.removeNotify(); // return ->  java.lang.NullPointerException
        //Platform.exit(); // -> does nothing

        System.err.println("i will never die!");
    }
    public void simpleFXoperations(){

        VBox vbox1 = new VBox();
        vbox1.setAlignment(Pos.BOTTOM_CENTER);
        vbox1.setStyle("-fx-border-style: solid;"
                + "-fx-border-width: 1;"
                + "-fx-border-color: white");
        System.err.println("simpleFXoperations() _DONE");
    }
}

и это нить, которая никогда не закрывается

"Прикрепить прослушиватель" - Тема t @17 java.lang.Thread.State: RUNNABLE

Заблокированные управляемые синхронизаторы:   - Нет

"Тема приложения JavaFX" - Тема t @13 java.lang.Thread.State: RUNNABLE на com.sun.glass.ui.gtk.GtkApplication._runLoop (родной Метод) при com.sun.glass.ui.gtk.GtkApplication $3 $1.run(GtkApplication.java:82)   в java.lang.Thread.run(Thread.java:722)

Заблокированные управляемые синхронизаторы:   - Нет

Обновление: Я использую новейший Oracle JDK 7u17 64bit на Linux Fedora 16 64bit.

Ответы

Ответ 1

Fix:

Мне удалось исправить эту проблему, вызвав com.sun.javafx.application.PlatformImpl.tkExit() непосредственно перед Platform.exit(). Я не очень хорошо понимаю источник JavaFX, но, похоже, он работает; YMMV.

Обновление: Выполнение этого в Java 8 приведет к предупреждению, вы можете просто отключить предупреждение с помощью @SuppressWarnings("restriction"). Это не должно быть проблемой.

Объяснение:

Я понял это, копая исходный код; JFXPanel имеет этот небольшой фрагмент (это от JavaFX 2.2.25)

finishListener = new PlatformImpl.FinishListener() {
  public void idle(boolean paramAnonymousBoolean) {
    if (!JFXPanel.firstPanelShown) {
      return;
    }
    PlatformImpl.removeListener(JFXPanel.finishListener);
    JFXPanel.access$102(null);
    if (paramAnonymousBoolean)
      Platform.exit();
  }

  public void exitCalled()
  {
  }

Проблема в том, что если вы используете только немного JavaFX в своем приложении, то метод idle(boolean) никогда ничего не делает (потому что firstPanelShown == false), что предотвращает удаление слушателя, что предотвращает JavaFX Toolkit от выключения... что означает, что вы должны отключить его вручную.

Ответ 2

Ваша основная функция не принадлежит объекту JavaFx Application, и я думаю, что ваша программа никогда не запустит цикл потока приложений.

Кажется, вам следует:

public class SOTestFX extends Application {
    public static void main(String[] args) {
        launch(args);
    }
    @Override
    public void start(Stage stage) throws Exception {
        // Do stuff here to show your stage or whatever you want;
        // This will be called on the JavaFX thread
    }
}

Ответ 3

Это скользкая ситуация, поскольку (насколько я понимаю) цель потока JavaFX заключается в прозрачном использовании различных аппаратных конвейеров. Могу ли я предложить разместить ваши запросы JavaFX в отдельный, ссылающийся, проект; и сохранить все остальное, включая ваш основной метод, в другом? Это всегда срабатывало для меня.

В принципе, бизнес-логика и модель идут в одном проекте, а просмотр и управление (как правило, основанные на JavaFX) идут в другом. Это позволяет осуществлять независимое завершение потока JavaFX. Надеюсь, это применимо к тому, что вы пытаетесь сделать.

Ответ 4

Я пробовал много всего, потому что ни один из вышеперечисленных ответов не работал у меня. Лучше всего для меня просто закрыть все JVM, используя

System.exit(1);

поскольку все мое приложение - это то, что одно приложение JavaFX и поэтому может быть отключено при закрытии этапа. 1 в моем случае является просто случайным int.