JavaFX Application (that uses a preloaded) exits prematurely
Asked Answered
T

1

6

I have a Java application that initializes with a Preloader. Once the Preloader hides, the main application starts and successfully loads the resources. The application window then briefly loads and then exits with no exceptions thrown.

Main application code:

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.adrian.mobsters.gui;

import com.adrian.mobsters.resource.Resource;
import com.adrian.mobsters.resource.ResourceFactory;
import java.io.IOException;
import java.util.logging.Level;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.application.Platform;
import javafx.application.Preloader.ProgressNotification;
import javafx.application.Preloader.StateChangeNotification;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

/**
 *
 * @author aelder
 */
public class MainGUI extends Application {

    BooleanProperty ready = new SimpleBooleanProperty(false);

    public void loadResources() {
        Task task = new Task<Void>() {

            @Override
            protected Void call() throws Exception {
                java.util.logging.Logger.getLogger("com.gargoylesoftware").setLevel(Level.OFF);
                System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.NoOpLog");

                double index = 0;
                for (ResourceFactory factory : ResourceFactory.values()) {
                    index++;
                    Resource resource = factory.getResource();

                    if (resource != null) {
                        resource.importResources();
                        notifyPreloader(new ProgressNotification(((double) index)/ResourceFactory.values().length));
                    }
                }

                ready.setValue(Boolean.TRUE);
                notifyPreloader(new StateChangeNotification(
                        StateChangeNotification.Type.BEFORE_START));
                return null;
            }
        };

        new Thread(task).start();
    }

    @Override
    public void start(Stage primaryStage) throws IOException {  
        loadResources();
        Parent root = FXMLLoader.load(MainGUI.class.getResource("main.fxml"));
        Scene scene = new Scene(root);

        primaryStage.setTitle("Mobsters Bot 2.0 - by Adrian Elder");
        primaryStage.setScene(scene);

        // After the app is ready, show the stage
        ready.addListener(new ChangeListener<Boolean>(){
            public void changed(
                ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
                    if (Boolean.TRUE.equals(t1)) {
                        Platform.runLater(new Runnable() {
                            public void run() {
                                primaryStage.show();
                            }
                        });
                    }
                }
        });;  
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }
}

The preloader code:

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package resourceloader;

import javafx.application.Preloader;
import javafx.application.Preloader.ProgressNotification;
import javafx.application.Preloader.StateChangeNotification;
import javafx.scene.Scene;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

/**
 * Simple Preloader Using the ProgressBar Control
 *
 * @author aelder
 */
public class ResourceLoader extends Preloader {

    ProgressBar bar;
    Stage stage;

    private Scene createPreloaderScene() {
        bar = new ProgressBar();
        BorderPane p = new BorderPane();
        p.setCenter(bar);
        return new Scene(p, 300, 150);      
    }

    @Override
    public void start(Stage stage) throws Exception {
        this.stage = stage;
        stage.setScene(createPreloaderScene());     
        stage.show();
    }

    @Override
    public void handleStateChangeNotification(StateChangeNotification scn) {
        if (scn.getType() == StateChangeNotification.Type.BEFORE_START) {
            stage.hide();
        }
    }

    @Override
    public void handleProgressNotification(ProgressNotification pn) {
        bar.setProgress(pn.getProgress());
    }   
}

Update: It seems it has something to do with executing the Stage show() method in the runLater method. For some reason, this causes the application to exit prematurely.

My current solution is to sleep until the resources have been loaded and then execute the show method on the application thread.

public void start(Stage primaryStage) throws IOException {
    this.primaryStage = primaryStage;

    primaryStage.setTitle("Mobsters Bot 2.0 - by Adrian Elder");
    primaryStage.getIcons().add(new Image(MainGUI.class.getResourceAsStream("/icons/sword.png")));

    currentThread = Thread.currentThread();
    primaryStage.setScene(new Scene(parent.get()));
    primaryStage.show();
}

@Override
public void init() throws InterruptedException {
    // After the app is ready, show the stage
    loadResources();

    while(!ready.get()) {
        Thread.sleep(100);
    }
}
Toluol answered 20/5, 2017 at 15:18 Comment(0)
O
2

Move your primaryStage.show(); out of the event and put it after primaryStage.setScene(scene); and see if the application is opening properly.. I think the problem is with the event that marking it as ready

This works for me.. Try commenting the logger and resource and then uncomment partly.. It will help in debug. As I do not have your libraries I can not do further.

public class Main extends Application {

    BooleanProperty ready = new SimpleBooleanProperty(false);

    public void loadResources() {
        Task task = new Task<Void>() {

            @Override
            protected Void call() throws Exception {
                //java.util.logging.Logger.getLogger("com.gargoylesoftware").setLevel(Level.OFF);
                //System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.NoOpLog");

                /*double index = 0;
                for (ResourceFactory factory : ResourceFactory.values()) {
                    index++;
                    Resource resource = factory.getResource();

                    if (resource != null) {
                        resource.importResources();
                        notifyPreloader(new ProgressNotification(((double) index) / ResourceFactory.values().length));
                    }
                }*/

                ready.setValue(Boolean.TRUE);
                notifyPreloader(new StateChangeNotification(StateChangeNotification.Type.BEFORE_START));
                return null;
            }
        };

        new Thread(task).start();
    }

    @Override
    public void start(Stage primaryStage) throws IOException {

        //Parent root = FXMLLoader.load(Main.class.getResource("main.fxml"));
        Scene scene = new Scene(new Label("Application started"), 400, 400);

        primaryStage.setTitle("Mobsters Bot 2.0 - by Adrian Elder");
        primaryStage.setScene(scene);

        // After the app is ready, show the stage
        ready.addListener(new ChangeListener<Boolean>() {
            public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
                if (Boolean.TRUE.equals(t1)) {
                    Platform.runLater(new Runnable() {
                        public void run() {
                            primaryStage.show();
                        }
                    });
                }
            }
        });
        loadResources();
    }

    /**
     * @param args
     *            the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }
}
Onions answered 20/5, 2017 at 15:28 Comment(6)
That fixes the issue but wouldn't this allow the application to start before the resources are done loading? I was following the tutorial by oracle (Figure 13-12) (docs.oracle.com/javase/8/docs/technotes/guides/deploy/…) and this event pattern was used.Toluol
I think the problem lies in your protected Void call() throws Exception function. Did you debug it ? I think add a try catch in that function and you should get an error. I can not debug your code as that refer some library I do not haveOnions
The error still happens even when removing all of my native code. Remove all code except for updating the progress bar notification to 1.0.Toluol
There must be an issue with my JDK or something... I used your code and it still didn't run.Toluol
hmm. May be... I am using 1.8 jdk which bydefault come with jfx librariesOnions
Let us continue this discussion in chat.Toluol

© 2022 - 2024 — McMap. All rights reserved.