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

查看:23
本文介绍了如何在预加载器中处理 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 完整跟踪

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

您应该会看到预加载器消息,这些消息应该为您提供有关正在发生的事情的一些信息.就我而言,我可以看到很多这样的消息

preloader: 添加待处理事件 2: DownloadEvent[type=load,loaded=0, total=62791, percent=0]

表示尚未验证/启动自定义预加载器,但下载事件已经进入.

如果将 <update check="background"/> 切换为 <update check="always"/> 会发生什么?

编辑

这是我的测试 JNLP.似乎您没有指定 JavaFX 运行时资源?

<信息><title>HelloWorldFX</title><vendor>未知</vendor><description>HelloWorldFX</description><离线允许/></信息><resources os="Windows"><jfx:javafx-runtime version="8.0+"/></资源><资源><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"/></资源><jfx:javafx-desc width="600" height="400" main-class="sample.Main" name="HelloWorldFX" preloader-class="HelloWorldPreloader"/><更新检查=总是"/></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天全站免登陆