現在、JavaFXベースのアプリケーションで作業しています。ユーザーは、ワールドマップ上にマークされた場所と対話できます。これを行うには、私はhttp://captaincasa.blogspot.de/2014/01/javafx-and-osm-openstreetmap.html([1])に記載されている方法と同様の方法を使用しています。ガベージコレクション後にJavascriptからJavaFx WebViewコールバックに失敗する
しかし、WebEngineのsetMember()メソッドを使用して埋め込みHTMLページに注入されるJavascriptコールバック変数に関連するデバッグが難しい問題に直面しています(公式チュートリアルではhttps://docs.oracle.com/javase/8/javafx/embedded-browser-tutorial/js-javafx.htm([2]も参照してください) 。
プログラムをしばらく実行すると、コールバック変数の状態が予期せず失われています。この動作を実証するために、最小限の作業/失敗の例を作成しました。私はWindows 10マシンで64ビットjdk1.8.0_121を使用しています。
次のようにJavaFXのアプリケーションが見えます:
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import javafx.application.Application;
import javafx.concurrent.Worker.State;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import netscape.javascript.JSObject;
public class WebViewJsCallbackTest extends Application {
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
public static void main(String[] args) {
launch(args);
}
public class JavaScriptBridge {
public void callback(String data) {
System.out.println("callback retrieved: " + data);
}
}
@Override
public void start(Stage primaryStage) throws Exception {
WebView webView = new WebView();
primaryStage.setScene(new Scene(new AnchorPane(webView)));
primaryStage.show();
final WebEngine webEngine = webView.getEngine();
webEngine.load(getClass().getClassLoader().getResource("page.html").toExternalForm());
webEngine.getLoadWorker().stateProperty().addListener((observableValue, oldValue, newValue) -> {
if (newValue == State.SUCCEEDED) {
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("javaApp", new JavaScriptBridge());
}
});
webEngine.setOnAlert(event -> {
System.out.println(DATE_FORMAT.format(new Date()) + " alerted: " + event.getData());
});
}
}
次のようにHTMLファイル "page.html" が見えます:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<!-- use for in-browser debugging -->
<!-- <script type='text/javascript' src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script> -->
<script type="text/javascript">
var javaApp = null;
function javaCallback(data) {
try {
alert("javaApp=" + javaApp + "(type=" + typeof javaApp + "), data=" + data);
javaApp.callback(data);
} catch (e) {
alert("caugt exception: " + e);
}
}
</script>
</head>
<body>
<button onclick="javaCallback('Test')">Send data to Java</button>
<button onclick="setInterval(function(){ javaCallback('Test'); }, 1000)">Send data to Java in endless loop</button>
</body>
</html>
コールバック変数javaApp
の状態は上をクリックすることによって観察することができます"Send data to Java in endless loop"
ボタンそれは、javaApp.callback
経由でコールバックメソッドを継続的に実行しようとします。これにより、Javaアプリケーションにログメッセージが生成されます。アラートは、物事を裏返すための追加のコミュニケーションチャネルとして使用されます(常にうまくいくように見えますが、回避策として現在使用されていますが、それはどういうものなのか...)。
なってすべてが動作している場合は、次の行に似のログを記録するたびに印刷する必要があります。
callback retrieved: Test
2017/01/27 21:26:11 alerted: [email protected]c693(type=object), data=Test
しかし、しばらく(2-7分から何か)した後、これ以上のコールバックが取得されていません、しかし、次の行のようなだけloggingsが印刷されています
2017/01/27 21:32:01 alerted: javaApp=undefined(type=object), data=Test
を変数を印刷する今Javaインスタンス・パスの代わりに'undefined'
を与えます。奇妙なことに、javaApp
の状態は本当に「未定義」ではないということです。 typeof
を使用するとobject
が返され、javaApp === undefined
はfalse
と評価されます。これは、コールバック呼び出しが例外をスローしないという事実に従っています(そうでなければ、"caugt exception: "
で始まるアラートが出力されます)。
Java VisualVMを使用すると、障害の時間はガベージコレクタがアクティブになった時間と一致することがわかりました。これは、ヒープメモリ消費量を見ることでわかります。 GCにより60MB〜16MB。
ここには何がありますか?どのように問題をさらにデバッグすることができますか?私は関連するバグを見つけることができませんでした...
ありがとうございました!
PS:リーフレット(cf [1])を介して世界地図を表示するためのJavaScriptコードを含むと、問題がずっと速く再現されました。ほとんどの場合、地図をすぐにロードまたはシフトすると、GCがその仕事をしました。このオリジナルの問題をデバッグする際には、ここで紹介した最小限の例を挙げました。
私はjdk 1.8.0_121と同じ問題がありました。それから、私はそれがjdk 1.8.0_60を実行している別のPCで動作することを認識しました。 1.8.0_60に切り替えられ、問題は解決されました。 –
@AliAyadJalil:古いバージョンに落ちることは、必ずしも望ましくない可能性があります。 : -/ –