почему javafx игнорирует возврат из main и все еще запускает приложение? - PullRequest
0 голосов
/ 06 сентября 2018

У меня есть следующий код.

public static void main(String[] args)
{
    if (!ArgumentsHandler.handle(args))
    {
        return;
    }

    Storage.getInstance().load();

    if (!Storage.getInstance().isLoadSuccessful())
    {
        launch(args);
    }
    else
    {
        System.err.println("Unable to load configurations.");
    }
}

Я специально инвертировал условие внутри оператора if для его сбоя, и я определенно вижу в отладчике, что он не выполняет метод launch, хотя окно приложения все еще отображается.

Я также заметил, что использование оператора return внутри main метода не имеет никакого эффекта - приложение все еще продолжает выполняться. Он отвечает только на System.exit(0).

Почему это происходит?

Обновление:

Как вы и просили, вот фрагмент ArgumentsHandler. Нигде здесь я не использую темы (по крайней мере, намеренно).

public static boolean handle(String[] args)
{
    //handle args
    if (args.length > 0)
    {
        switch (args[0])
        {
            //createRepository
            case "-c":
                configure(args);
                break;
            case "-r":
            case "--repository":
                repository(args);
                break;
            default:
                help();
                break;
        }

        return false;
    }

    return true;
}

private static void configure(String[] args)
{
    if (args.length > 1)
    {
        boolean isRandom = false;

        switch (args[1])
        {
            case "true":
            case "1":
                isRandom = true;
                break;
            case "false":
            case "0":
                //valid input, ignored
                break;
            default:
                System.err.println("Invalid arguments. Possible values: [--configuration] [1/0].");
                return;
        }

        Storage.configure(isRandom); //creates a bunch of json files (uses NIO).
        return;
    }
    else
    {
        System.err.println("Invalid arguments. Possible values: -c [1/0].");
    }
}

Хранение

public void load()
{
    isLoadSuccessful = false;

    //load configuration
    app = loadConfiguration(appFilePath);

    if (app == null)
    {
        System.err.println("Unable to load app configuration.");
        return;
    }

    //load company
    company = loadCompany(app.getCompanyFilePath());

    if (company == null)
    {
        System.err.println("Unable to load company configuration.");
        return;
    }

    repository = loadRepository(app.getRepositoryFilePath());

    if (repository == null)
    {
        System.err.println("Unable to load repository configuration.");
        return;
    }

    isLoadSuccessful = true;
}

private static App loadConfiguration(String filePath)
{
    return (App) Utility.load(filePath, App.class);
}

loadConfiguration, loadCompany и loadRepository действительно одинаковы. В будущем они не будут читать простые файлы json, но получат доступ к сложным архивам, поэтому я уже создал несколько почти идентичных методов.

Utility.load

public static Object load(String path, Type type)
{
    try
    {
        JsonReader reader = new JsonReader(new FileReader(path));
        Gson gson = new Gson();
        Object obj = gson.fromJson(reader, type);
        reader.close();

        return obj;
    }
    catch (IOException ex)
    {
        ex.printStackTrace();
        return null;
    }
}

Просто десериализует объект из файла.

1 Ответ

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

Исходя из того, как вы звоните launch(args) Я предполагаю, а вы позже подтвердили , что метод main находится в подклассе Application. Я считаю, что это является причиной вашей проблемы.

Как вы заметили, запущено много, по-видимому, специфичных для JavaFX потоков. В частности, работает не-демон «Поток приложения JavaFX» (по крайней мере, он не является демоном в Java 10). Этот поток заставит JVM остаться в живых, даже если поток main завершится. Это нормальное поведение для Java:

java.lang.Thread

Когда запускается виртуальная машина Java, обычно существует один поток, не являющийся демоном (который обычно вызывает метод с именем main некоторого назначенного класса). Виртуальная машина Java продолжает выполнять потоки, пока не произойдет одно из следующих событий:

  • Вызван метод exit класса Runtime, и диспетчер безопасности разрешил выполнение операции выхода.
  • Все потоки, которые не являются потоками демонов, умерли, либо возвращаясь из вызова к методу run, либо выбрасывая исключение, которое распространяется за пределы метода run.

Но почему запускается "Поток приложений JavaFX", когда вы сознательно еще не вызвали Application.launch? Я просто догадываюсь здесь, но это, вероятно, связано со специальной обработкой, которую получают приложения JavaFX. Поскольку по крайней мере Java 8 вам не нужно объявлять метод main внутри подкласса Application 1 . Если основной класс является подклассом Application, Java выполняет запуск автоматически.

import javafx.application.Application;
import javafx.stage.Stage;

public class MyApp extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        // create scene and show stage...
    }

}

Если у вас есть вышеперечисленное и позвоните java MyApp, приложение запустится и будет вызвано start. Тем не менее, если у вас есть ниже:

import javafx.application.Application;
import javafx.stage.Stage;

public class MyApp extends Application {

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        // create scene and show stage...
    }

}

Затем вызывается метод main, но start - это , а не . По сути, явное объявление main отменяет поведение по умолчанию при запуске приложения JavaFX , но не мешает инициализации среды выполнения JavaFX . Может быть, это поведение, как задумано, или, возможно, это упущение Но важно то, что это происходит только в том случае, если основной класс имеет метод main и является подклассом Application. Если вы разделите эти два:

public class MyApp extends Application {
    // implement ...
}

public class Main {
    public static void main(String[] args) {
        // Perform pre-checks, return if necessary
        Application.launch(MyApp.class, args);
    }
}

Тогда у вас больше не будет этой проблемы.

В противном случае вы можете продолжить использование System.exit() или переключиться на Platform.exit().


Есть другой, возможно, более подходящий способ справиться с этим. Похоже, вы выполняете инициализацию в методе main перед вызовом Application.launch. Если что-то пойдет не так во время этой инициализации, вы захотите прервать запуск приложения JavaFX. Ну, JavaFX предоставляет средства, чтобы сделать это самостоятельно: Application.init().

Метод инициализации приложения. Этот метод вызывается сразу после загрузки и создания класса Application. Приложение может переопределить этот метод для выполнения инициализации до фактического запуска приложения.

Реализация этого метода, предоставляемого классом Application, ничего не делает.

ПРИМЕЧАНИЕ. Этот метод не вызывается в потоке приложений JavaFX. Приложение не должно создавать сцену или сцену в этом методе. Приложение может создавать другие объекты JavaFX в этом методе.

Переместите код инициализации в этот метод. Если вы позвоните по номеру Platform.exit(), приложение закроется, и Application.start не будет вызвано. Альтернатива - бросить исключение внутри init. Вы также можете получить аргументы приложения, используя Application.getParameters(), который возвращает экземпляр Application.Parameters.

public class MyApp extends Application {

    @Override
    public void init() throws Exception {
        if (!ArgumentsHandler.handle(getParameters()) {
            Platform.exit(); // or throw an exception
        } else {
            Storage storage = Storage.getInstance();
            storage.load();
            if (!storage.isLoadSuccessful()) {
                Platform.exit(); // or throw an exception
            }
        }
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        // Create Scene and show the primary Stage
    }

    @Override
    public void stop() throws Exception {
        /*
         * Called when the JavaFX application is exiting, such as from
         * a call to Platform.exit(). Note, however, that in my experience
         * this method is *not* called when Platform.exit() is called inside
         * the "init" method. It is called if Platform.exit() is called from
         * inside the "start" method or anywhere else in the application once
         * it is properly started.
         *
         * This is where you could perform any necessary cleanup.
         */
    }

}

1. JavaFX был включен в Java SE в версии 8. Обратите внимание, что это поведение может измениться в Java 11, поскольку JavaFX должен снова отделиться от Java SE.

...