アプリケーション固有の初期化を処理するアプリケーション用のプリローダーがあります。今、私はこれを拡張して、プリローダーがダウンロードされたアプリケーションJARの進行状況も表示するようにしようとしています。
フェーズ2中にプリローダーがロードされないのはなぜですか?これはPreloaderFx::handleProgressNotification();
を処理して、JARのダウンロードを追跡する必要があるためです。 ?
2016年3月14日更新:これを解決する方法はDownloadServiceListenerを使用していますか?これをJavaFXステージに接続するにはどうすればよいですか?
Oracleによると 、アプリケーションが起動されるとき、4つのフェーズがあります:
フェーズ2:ロードと準備:必要なリソースがネットワークまたはディスクキャッシュからロードされ、検証手順が実行されます。すべての実行モードには、デフォルトまたはカスタムのプリローダーが表示されます。このフェーズでは、カスタムプリローダーが表示されます。
フェーズ3:アプリケーション固有の初期化:アプリケーションは開始されますが、完全に機能する前に、追加のリソースをロードするか、他の長い準備を実行する必要がある場合があります。現時点では、カスタムプリローダーが表示されています。
私が最初に気付くのは、フェーズ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<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>
_
ファイルを起動するときに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
ブール値に関連するすべてをコメントアウトしました(nhulatedによって提案されました)System.out.println()
を追加しましたJNLPファイルに_<jfx:javafx-runtime version="8.0+"/>
_を追加するだけで修正されました。その行が追加されると、プリローダーはフェーズ2で表示されます。また、自由に_j2se version="1.6+"
_を_j2se version="1.8+"
_に変更しました。結果は次のとおりです。
最初の50%は、JARダウンロードの処理です。これは、handleProgressNotification()
メソッドによって実行されます。 2番目の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
何が起こっているかについての情報を提供するプリローダーメッセージが表示されるはずです。私の場合、このようなメッセージをたくさん見ることができました
preloader: Added pending event 2: DownloadEvent[type=load,loaded=0, total=62791, percent=0]
カスタムプリローダーがまだ検証/開始されていないが、ダウンロードイベントがすでに発生していることを示します。
<update check="background"/>
を<update check="always"/>
に切り替えるとどうなりますか?
これは私のテストJNLPです。 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>
注:コードをテストまたは実行していません。私の答えは、主にコードを見ることに基づいています。私はJavaFXに取り組んだことはありませんが、以前にSwingとJNLPに取り組んだことがあるので、コードを理解できます。
私が最初に気付くのは、フェーズ2では、アプリケーションJARのダウンロードを処理するデフォルトのJavaFXプリローダーが表示されていないことです。
これは、PreloaderFX.start(stage)
メソッドが原因のようです。メソッドがnew Stage()
を作成し、それに子を追加するため、メソッドの引数として渡されたstage
は空のままです。メソッドの引数に子要素を追加すると、フェーズ2でカスタムプリローダー/プログレスバーが表示されます。
この後もコードが期待どおりに機能しない場合は、noLoadingProgress
フラグに関連付けられているすべてのロジック/コードをデバッグしてみます。それらすべてをコメントアウトして(基本的には画像からnoLoadingProgress
を削除して)、問題が解決するかどうかを確認してください。
また、コードにこれが正しく含まれている場合でも、 this answer-を参照してください。すべてのProgressNotification
sはhandleApplicationNotification
メソッドによって処理されます。
お役に立てれば!