JavaFX的WebEngine的JavaScript向上调用
问题描述:
在java中Windows 10 pro x64 jre 1.8.0_60
下面的代码产生预期输出(点击HTML按钮后):JavaFX的WebEngine的JavaScript向上调用
Hello World
但是在java中Windows 10 pro x64 jre 1.8.0_152
似乎有某种脱节的,因为它不会输出任何东西点击按钮控制台
为什么最新版本的java(在152)是我的代码给出不可预知的,通常不需要的结果。我试图给出最小代码来创建下面的场景。
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import netscape.javascript.JSObject;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
WebView browser = new WebView();
final WebEngine webEngine = browser.getEngine();
webEngine.setJavaScriptEnabled(true);
webEngine.load("https://api.ipify.org/?format=json");
webEngine.getLoadWorker().stateProperty().addListener(
new ChangeListener<Worker.State>() {
@Override
public void changed(ObservableValue ov, Worker.State oldState, Worker.State newState) {
if (newState == Worker.State.SUCCEEDED) {
JSObject jso = (JSObject) webEngine.executeScript("window");
webEngine.executeScript(
"var button = document.createElement(\"button\");\n" +
"button.innerHTML = \"Do Something\";\n" +
"var body = document.getElementsByTagName(\"body\")[0];\n" +
"body.appendChild(button);\n" +
"button.addEventListener (\"click\", function() {java.exit();});");
jso.setMember("java", new Bridge());
}
}
});
BorderPane panel = new BorderPane(browser);
Scene scene = new Scene(panel, 700, 700);
primaryStage.setScene(scene);
primaryStage.show();
}
public class Bridge {
public void exit() {
System.out.println("Hello World");
}
}}
答
您正在将new Bridge()
传递给setMember方法。由于没有变量保存Bridge实例,所以在你按下按钮之前它会被垃圾收集。
注意,在上面的例子中,该应用程序保持到
JavaApplication
实例的引用。这是从JavaScript执行所需方法的回调所必需的。在下面的示例中,应用程序不保持到Java对象的引用:
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("app", new JavaApplication());
在这种情况下,由于属性值是一个局部对象,
"new JavaApplication()"
,值可能在下一个GC循环中被垃圾收集。当用户点击链接时,它不保证执行回调方法
exit
。
让您大桥对象在一个领域,以防止它的垃圾收集:
new ChangeListener<Worker.State>() {
private final Bridge bridge = new Bridge();
@Override
public void changed(ObservableValue ov, Worker.State oldState, Worker.State newState) {
if (newState == Worker.State.SUCCEEDED) {
// ...
jso.setMember("java", bridge);
}
}
它为什么发生在早期版本的Java?因为不同的Java版本可以*改变垃圾收集的时间和行为。你很幸运,但在后来的版本中,你的运气已经耗尽。
因此'bridge'成为新的匿名ChangeListener实例的类属性。 ObservableValue'addListener'方法对新的ChangeListener实例持有强烈的引用,以防止它被垃圾收集,从而阻止了Bridge实例被垃圾收集? –
那是关于它的,是的。 – VGR