如何在预加载器中处理java web start(jnlp)下载进度? [英] How to handle java web start (jnlp) downloading progress in a preloader?

查看:95
本文介绍了如何在预加载器中处理java web start(jnlp)下载进度?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题



我的应用程序的预加载器处理特定于应用程序的初始化。现在我正在尝试对此进行扩展,以便预加载器还显示下载的应用程序JAR的进度。






TL; DR




  • 为什么预加载器在阶段2 期间没有加载,因为这应该处理 PreloaderFx :: handleProgressNotification(); 跟踪我猜想的JAR下载?


  • 2016年3月14日更新:是否正在使用DownloadServiceListener来解决这个问题?如何将它连接到JavaFX阶段?







文档






  • 阶段2:加载和准备:从网络或磁盘缓存加载所需的资源,并进行验证过程。所有执行模式都会看到默认或自定义预加载器。在此阶段,应显示我的自定义预加载器。


  • 阶段3:特定于应用程序的初始化:应用程序已启动,但它可能需要加载额外的资源或执行其他冗长的准备工作才能完全正常运行。目前,我的自定义预加载器显示为:







  • 阶段4:应用程序执行:显示应用程序并准备使用。在我的情况下,会显示一个登录窗口,用户可以继续。








我的案例



我注意到的第一件事就是阶段2 ,处理下载应用程序JAR的默认JavaFX预加载器未显示。因此,用户感觉程序没有提前启动或终止,使他们多次打开JNLP文件。下载JAR后,我们输入阶段3 并显示预加载器。



但是,我希望我的自定义预加载器能够处理同时下载ProgressBar中的进度(阶段2)。我使一切尽可能简单,以便在应用程序启动期间跟踪发生的事件。这是基于



前50%是JAR下载的处理。这是通过 handleProgressNotification()方法完成的。第二个50%是实际初始化MainApp( longstart(),它通知预加载器),由 handleApplicationNotification()

解决方案

我最近也一直在与此作斗争。我切换回(丑陋的)默认预加载器(因为它很好地显示),直到我找到更多时间来研究它。



如果启用Java Webstart完全跟踪

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

you应该看到预加载器消息,它应该为您提供有关正在发生的事情的一些信息。在我的情况下,我可以看到很多这样的消息

  preloader:添加待处理事件2:DownloadEvent [type = load,loaded = 0,总计= 62791,百分比= 0] 

表示自定义预加载器尚未经过验证/尚未开始,但下载活动已经开始。



如果您切换< update check =background/> < update check =always/>



编辑



这是我的测试JNLP。您似乎缺少指定JavaFX运行时资源?

 <?xml version =1.0encoding =utf -8\" >?; 
< jnlp spec =1.0+xmlns:jfx =http://javafx.comcodebase =http:// localhost:8080 / HelloWorldFXhref =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.jarsize =10774download =progress/>
< jar href =HelloWorldFX.jarsize =248884114download =eagermain =true/>
< / resources>
< jfx:javafx-desc width =600height =400main-class =sample.Mainname =HelloWorldFXpreloader-class =HelloWorldPreloader/>
< update check =always/>
< / jnlp>


Issue

I have a preloader for my application that handles the Application-specific initialization. Now I'm trying to extend this so that the preloader also shows the progress of the downloaded application JARs.


TL;DR

  • Why is the preloader not getting loaded during Phase 2, as this should handle the PreloaderFx::handleProgressNotification(); to track the downloading of the JARs I suppose?

  • Update 14 March 2016: Is using DownloadServiceListener the way to solve this? How to connect this to a JavaFX stage?


Documentation

According to Oracle, there are 4 phases when an application gets launched:

  • Phase 1: Initialization: Initialization of Java Runtime and an initial examination identifies components that must be loaded and executed before starting the application. During this phase, a splash screen is shown. Default this is this:

  • Phase 2: Loading and preparation: The required resources are loaded from either the network or a disk cache, and validation procedures occur. All execution modes see the default or a custom preloader. During this phase, my custom preloader should be shown.

  • Phase 3: Application-specific initialization: The application is started, but it may need to load additional resources or perform other lengthy preparations before it becomes fully functional. At the moment, my custom preloader is shown:

  • Phase 4: Application execution: The application is displayed and is ready to use. In my case, a login window is shown and the user can proceed.


My Case

The first thing I notice, is that in Phase 2, the default JavaFX preloader handling the downloading of the application JARs is not showing. Because of this, the user gets the feeling the program did not start or terminated prematurely, making them to open the JNLP file multiple times. Once the JARs are downloaded, we enter Phase 3 and the preloader is shown.

However, I'd like my custom preloader to handle the downloading progress in the ProgressBar as well (Phase 2). I made everything as simple as possible to track down which events are happening during the start-up of my application. This is based on an example of Jewelsea and on Oracle examples:

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();
            }
        }
    }

Code that is being handled in Phase 3 is from the main application who interacts with the preloader, this is what is being seen in the progressbar:

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>


Debugging

I closely watched the Java Console when launching the file (with Show logging enabled, Show tracing disabled) and noticed following things:

During Phase 2, nothing shows up in the Java Console (console closes after this phase)

During Phase 3, following output is generated (in a new console window):

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


Updates 13 March 2016:

  • Adjusted the code so the stage passed in the method is used rather than creating a new one and commented out everything related to the noLoadingProgress boolean (suggested by nhylated)
  • Added some extra System.out.println() in the MainApp

Solution

Simple adding <jfx:javafx-runtime version="8.0+"/> to the JNLP file fixed it. With that line added, the preloader shows in Phase 2. I also took the liberty to change the j2se version="1.6+" to j2se version="1.8+" The result:

The first 50% is the handling of the JAR downloads. This is done by the handleProgressNotification() method. The second 50% is the actual initializing of the MainApp (longstart() which notifies the preloader), done by the handleApplicationNotification().

解决方案

I have been fighting with this too, recently. I switched back to the (ugly) default preloader (as that one shows up nicely) until I find some more time to investigate this.

If you enable Java Webstart full tracing

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

you should see preloader messages which should give you some information about what is going on. In my case I could see a lot of messages like these

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

indicating that the custom preloader hasn't been verified/started yet but download events were already coming in.

What happens if you switch <update check="background"/> to <update check="always"/>?

EDIT

This is my test JNLP. Seems like you're missing to specify the JavaFX runtime resource?

<?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>

这篇关于如何在预加载器中处理java web start(jnlp)下载进度?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆