问题

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

TL; DR

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

2016年3月14日更新 :使用DownloadServiceListener解决这个问题的方法是什么? 如何将其连接到JavaFX阶段?

文档

根据Oracle的说法 ,启动应用程序有4个阶段:

阶段1:初始化 :Java Runtime的初始化和初始检查确定在启动应用程序之前必须加载和执行的组件。 在此阶段,将显示启动画面。 默认是这样的:

jnlp 无法使用_java 资源预加载

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

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

阶段4:应用程序执行 :显示应用程序并准备使用。 在我的例子中,显示了一个登录窗口,用户可以继续。

jnlp 无法使用_预加载_02

我的情况

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

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

预载:

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

阶段3中正在处理的代码来自与预加载器交互的主应用程序,这是进度条中显示的内容:

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() {
@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
...
... //whole bunch of JARS

调试

我在启动文件时密切关注Java控制台(启用了显示日志记录,禁用了显示跟踪)并注意到以下事项:

在阶段2期间,Java控制台中没有任何内容显示(控制台在此阶段后关闭)

在阶段3期间,生成以下输出(在新的控制台窗口中):

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

更新2016年3月13日:

调整代码,以便使用方法中传递的阶段而不是创建新的代码并注释掉与noLoadingProgress布尔相关的所有内容(由nhylated建议)

在MainApp中添加了一些额外的System.out.println()

简单地将到JNLP文件中修复它。 添加该行后,预加载器显示在第2阶段。我也冒昧地将j2se version="1.6+"更改为j2se version="1.8+"结果:

jnlp 无法使用_ide_03

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