javafx-8 alternative - Come gestire java web start(jnlp)scaricando i progressi in un preloader?




tm launcher (3)

Ho litigato anche con questo, di recente. Sono tornato al (brutto) predefinito preloader (come quello si presenta bene) finché non trovo più tempo per indagare su questo.

Se si abilita la traccia completa di Java Webstart

"<JAVA_HOME>\bin\javaws.exe" -userConfig deployment.trace true
"<JAVA_HOME>\bin\javaws.exe" -userConfig deployment.trace.level all

dovresti vedere messaggi di preloader che dovrebbero darti qualche informazione su cosa sta succedendo. Nel mio caso ho potuto vedere molti messaggi come questi

preloader: Added pending event 2: DownloadEvent[type=load,loaded=0, total=62791, percent=0]

indicando che il preloader personalizzato non è stato ancora verificato / avviato ma gli eventi di download erano già in arrivo.

Cosa succede se si passa da <update check="background"/> a <update check="always"/> ?

MODIFICARE

Questo è il mio test JNLP. Sembra che manchi di specificare la risorsa di runtime JavaFX?

<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.0+" xmlns:jfx="http://javafx.com" codebase="http://localhost:8080/HelloWorldFX" href="HelloWorldFX.jnlp">
  <information>
    <title>HelloWorldFX</title>
    <vendor>Unknown</vendor>
    <description>HelloWorldFX</description>
    <offline-allowed/>
  </information>
  <resources os="Windows">
        <jfx:javafx-runtime version="8.0+"/>
    </resources>
  <resources>
    <j2se version="1.8+" href="http://java.sun.com/products/autodl/j2se"/>
    <jar href="HelloWorldPreloader.jar" size="10774" download="progress" />
    <jar href="HelloWorldFX.jar" size="248884114" download="eager" main="true" />
  </resources>
  <jfx:javafx-desc  width="600" height="400" main-class="sample.Main"  name="HelloWorldFX"  preloader-class="HelloWorldPreloader"/>
  <update check="always"/>
</jnlp>

Problema

Ho un preloader per la mia applicazione che gestisce l'inizializzazione specifica dell'applicazione. Ora sto cercando di estenderlo in modo che anche il preloader mostri l'avanzamento dei JAR dell'applicazione scaricati.

TL; DR

  • Perché il preloader non viene caricato durante la fase 2 , in quanto questo dovrebbe gestire PreloaderFx::handleProgressNotification(); suppongo di tenere traccia del download dei JARs?

  • Aggiornamento 14 marzo 2016 : utilizza DownloadServiceListener come risolvere questo? Come collegarlo a uno stage JavaFX?

Documentazione

Secondo Oracle , ci sono 4 fasi quando viene lanciata un'applicazione:

  • Fase 1: Inizializzazione : l'inizializzazione di Java Runtime e un esame iniziale identificano i componenti che devono essere caricati ed eseguiti prima di avviare l'applicazione. Durante questa fase, viene visualizzata una schermata iniziale. Predefinito questo è questo:

  • Fase 2: caricamento e preparazione : le risorse richieste vengono caricate dalla rete o dalla cache del disco e si verificano procedure di convalida. Tutte le modalità di esecuzione visualizzano il predefinito o un preloader personalizzato. Durante questa fase, dovrebbe essere mostrato il mio preloader personalizzato.

  • Fase 3: inizializzazione specifica dell'applicazione: l'applicazione viene avviata, ma potrebbe essere necessario caricare risorse aggiuntive o eseguire altri lunghi preparativi prima che diventi pienamente funzionante. Al momento, viene mostrato il mio preloader personalizzato:

  • Fase 4: esecuzione dell'applicazione : l'applicazione viene visualizzata ed è pronta per l'uso. Nel mio caso, viene visualizzata una finestra di accesso e l'utente può procedere.

Il mio caso

La prima cosa che noto è che nella Fase 2 , il preloader JavaFX predefinito che gestisce il download dei JAR dell'applicazione non viene visualizzato. Per questo motivo, l'utente ha la sensazione che il programma non sia stato avviato o terminato prematuramente, facendoli aprire più volte il file JNLP. Una volta scaricati i JAR, entriamo in Fase 3 e viene mostrato il preloader.

Tuttavia, mi piacerebbe che il mio preloader personalizzato gestisse i progressi del download anche in ProgressBar (fase 2). Ho reso tutto il più semplice possibile per rintracciare quali eventi accadono durante l'avvio della mia applicazione. Questo è basato su un esempio di Jewelsea e su esempi Oracle :

preloader:

public class PreloaderFX extends Preloader {

        Stage stage;
        //boolean noLoadingProgress = true;

        public static final String APPLICATION_ICON
            = "http://cdn1.iconfinder.com/data/icons/Copenhagen/PNG/32/people.png";
        public static final String SPLASH_IMAGE
            = "http://fxexperience.com/wp-content/uploads/2010/06/logo.png";

        private Pane splashLayout;
        private ProgressBar loadProgress;
        private Label progressText;
        private static final int SPLASH_WIDTH = 676;
        private static final int SPLASH_HEIGHT = 227;

        @Override
        public void init() {
            ImageView splash = new ImageView(new Image(
                SPLASH_IMAGE
            ));
            loadProgress = new ProgressBar();
            loadProgress.setPrefWidth(SPLASH_WIDTH - 20);
            progressText = new Label("Loading . . .");
            splashLayout = new VBox();
            splashLayout.getChildren().addAll(splash, loadProgress, progressText);
            progressText.setAlignment(Pos.CENTER);
            splashLayout.setStyle(
                "-fx-padding: 5; "
                + "-fx-background-color: white; "
                + "-fx-border-width:5; "
            );
            splashLayout.setEffect(new DropShadow());
        }

        @Override
        public void start(Stage stage) throws Exception {
            System.out.println("PreloaderFx::start();");

            //this.stage = new Stage(StageStyle.DECORATED);
            stage.setTitle("Title");
            stage.getIcons().add(new Image(APPLICATION_ICON));
            stage.initStyle(StageStyle.UNDECORATED);
            final Rectangle2D bounds = Screen.getPrimary().getBounds();
            stage.setScene(new Scene(splashLayout));
            stage.setX(bounds.getMinX() + bounds.getWidth() / 2 - SPLASH_WIDTH / 2);
            stage.setY(bounds.getMinY() + bounds.getHeight() / 2 - SPLASH_HEIGHT / 2);
            stage.show();

            this.stage = stage;
        }

        @Override
        public void handleProgressNotification(ProgressNotification pn) {
            System.out.println("PreloaderFx::handleProgressNotification(); progress = " + pn.getProgress());
            //application loading progress is rescaled to be first 50%
            //Even if there is nothing to load 0% and 100% events can be
            // delivered
            if (pn.getProgress() != 1.0 /*|| !noLoadingProgress*/) {
                loadProgress.setProgress(pn.getProgress() / 2);
                /*if (pn.getProgress() > 0) {
                noLoadingProgress = false;
                }*/
            }
        }

        @Override
        public void handleStateChangeNotification(StateChangeNotification evt) {
            //ignore, hide after application signals it is ready
            System.out.println("PreloaderFx::handleStateChangeNotification(); state = " + evt.getType());
        }

        @Override
        public void handleApplicationNotification(PreloaderNotification pn) {
            if (pn instanceof ProgressNotification) {
                //expect application to send us progress notifications 
                //with progress ranging from 0 to 1.0
                double v = ((ProgressNotification) pn).getProgress();
                System.out.println("PreloaderFx::handleApplicationNotification(); progress = " + v);
                //if (!noLoadingProgress) {
                //if we were receiving loading progress notifications 
                //then progress is already at 50%. 
                //Rescale application progress to start from 50%               
                v = 0.5 + v / 2;
                //}
                loadProgress.setProgress(v);
            } else if (pn instanceof StateChangeNotification) {
                System.out.println("PreloaderFx::handleApplicationNotification(); state = " + ((StateChangeNotification) pn).getType());
                //hide after get any state update from application
                stage.hide();
            }
        }
    }

Il codice che viene gestito in Phase 3 proviene dall'applicazione principale che interagisce con il preloader, questo è ciò che viene visualizzato nella barra di avanzamento:

public class MainApp extends Application {
    BooleanProperty ready = new SimpleBooleanProperty(false);

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

    @Override
    public void start(final Stage initStage) throws Exception {
        System.out.println("MainApp::start();");
        this.mainStage = initStage;

        longStart();

        ready.addListener((ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) -> {
            if (Boolean.TRUE.equals(t1)) {
                Platform.runLater(() -> {
                    System.out.println("MainApp::showMainStage();");
                    showMainStage();
                });
            }
        });   
    }

    private void longStart() {
        //simulate long init in background
        Task task = new Task<Void>() {
            @Override
            protected Void call() throws Exception {
                int max = 10;
                for (int i = 1; i <= max; i++) {
                    Thread.sleep(500);
                    System.out.println("longStart " + i);
                    // Send progress to preloader
                    notifyPreloader(new ProgressNotification(((double) i)/max)); //this moves the progress bar of the preloader
                }
                // After init is ready, the app is ready to be shown
                // Do this before hiding the preloader stage to prevent the 
                // app from exiting prematurely
                ready.setValue(Boolean.TRUE);

                notifyPreloader(new StateChangeNotification(
                    StateChangeNotification.Type.BEFORE_START));

                return null;
            }
        };
        new Thread(task).start();
    }

    private void showMainStage() {
        //showing the login window
    }
}

JNLP

<jnlp spec="1.0+" xmlns:jfx="http://javafx.com" codebase="<***>/preloadertest/jnlp" href="launch.jnlp">
    <information>
        ...
    </information>
    <resources>
        <j2se version="1.6+" href="http://java.sun.com/products/autodl/j2se" />


        ... //whole bunch of JARS

        <jar href="lib/preloader-1.1.1.jar" download="progress" />


    </resources>
    <security>
        <all-permissions/>
    </security>
    <applet-desc width="1024" height="768" main-class="com.javafx.main.NoJavaFXFallback" name="JavaFX Client">
        <param name="requiredFXVersion" value="8.0+"/>
    </applet-desc>
    <jfx:javafx-desc width="1024" height="768" main-class="GUI.MainApp" name="JavaFX Client" preloader-class="GUI.PreloaderFX" />
    <update check="background"/>
</jnlp>

Debug

Ho osservato da vicino la console Java all'avvio del file (con Mostra registrazione abilitata, Mostra traccia disabilitata) e ho notato quanto segue:

Durante la fase 2 , nulla viene visualizzato nella console Java (la console si chiude dopo questa fase)

Durante la fase 3 , viene generato il seguente output (in una nuova finestra della console):

PreloaderFx::start();
PreloaderFx::handleProgressNotification(); progress = 1.0
PreloaderFx::handleStateChangeNotification(); state = BEFORE_LOAD
PreloaderFx::handleStateChangeNotification(); state = BEFORE_INIT
PreloaderFx::handleStateChangeNotification(); state = BEFORE_START
MainApp::start();
MainApp::longstart();
longStart 1
PreloaderFx::handleApplicationNotification(); progress = 0.1
longStart 2
PreloaderFx::handleApplicationNotification(); progress = 0.2
longStart 3
PreloaderFx::handleApplicationNotification(); progress = 0.3
longStart 4
PreloaderFx::handleApplicationNotification(); progress = 0.4
longStart 5
PreloaderFx::handleApplicationNotification(); progress = 0.5
longStart 6
PreloaderFx::handleApplicationNotification(); progress = 0.6
longStart 7
PreloaderFx::handleApplicationNotification(); progress = 0.7
longStart 8
PreloaderFx::handleApplicationNotification(); progress = 0.8
longStart 9
PreloaderFx::handleApplicationNotification(); progress = 0.9
longStart 10
PreloaderFx::handleApplicationNotification(); progress = 1.0
MainApp::showMainStage();
PreloaderFx::handleApplicationNotification(); state = BEFORE_START

Aggiornamenti 13 marzo 2016:

  • Aggiustato il codice in modo che lo stage passato nel metodo sia usato piuttosto che crearne uno nuovo e commentato tutto ciò che è relativo al noLoadingProgress booleano (suggerito da nylato)
  • Aggiunti alcuni extra System.out.println() nella MainApp

Soluzione

Aggiunta semplice <jfx:javafx-runtime version="8.0+"/> al file JNLP risolto. Con questa linea aggiunta, il preloader mostra in Fase 2. Mi sono anche preso la libertà di cambiare la j2se version="1.6+" in j2se version="1.8+" Il risultato:

Il primo 50% è la gestione dei download di JAR. Questo viene fatto dal metodo handleProgressNotification() . Il secondo 50% è l'effettiva inizializzazione della MainApp ( longstart() che notifica il preloader), eseguita da handleApplicationNotification() .


Nota: non ho testato o eseguito il codice; la mia risposta si basa principalmente sull'osservazione del codice. Non ho lavorato su JavaFX ma riesco a cogliere il codice da quando ho lavorato su Swing e JNLP in precedenza.

La prima cosa che noto è che nella Fase 2, il preloader JavaFX predefinito che gestisce il download dei JAR dell'applicazione non viene visualizzato.

Questo sembra essere dovuto al metodo PreloaderFX.start(stage) . La stage passata in quanto l'argomento del metodo rimane vuoto perché il metodo costruisce un new Stage() e aggiunge dei child ad esso. Se aggiungi gli elementi figlio all'argomento del metodo, dovrebbe visualizzare il tuo preloader / barra di avanzamento personalizzato durante la fase 2.

Se il codice continua a non funzionare come previsto, proverei a eseguire il debug di tutta la logica / codice associata al flag noLoadingProgress . Prova a commentare tutto questo (essenzialmente togliendo noLoadingProgress dall'immagine) e verifica se risolve il problema.

Inoltre, anche se hai questo diritto nel tuo codice, vedi this risposta - secondo cui tutti i ProgressNotification sono gestiti dal metodo handleApplicationNotification .

Spero che questo ti aiuti!


se vuoi semplicemente evitare gli inconvenienti della codifica Java in JSP, puoi farlo anche con gli scriplet. Basta seguire un po 'di disciplina per avere Java minimo in JSP e quasi nessun calcolo e logica nella pagina JSP.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%//instantiate a JSP controller
MyController clr = new MyController(request, response);

//process action if any
clr.process(request);

//process page forwaring if necessary

//do all variable assignment here
String showMe = clr.getShowMe();%>

<html>
    <head>
    </head>
    <body>
        <form name="frm1">
            <p><%= showMe %>
            <p><% for(String str : clr.listOfStrings()) { %>
            <p><%= str %><% } %>

            // and so on   
        </form>
    </body>
</html>




java javafx-8 java-web-start jnlp