JavaFXWebEngine JavaScript向上调用

JavaFXWebEngine JavaScript向上调用,javascript,java,javafx,webengine,Javascript,Java,Javafx,Webengine,在javaWindows 10 pro x64 jre 1.8.0_60中,以下代码生成预期输出(单击html按钮后): 但是,在javaWindows 10 pro x64 jre 1.8.0_152中,似乎存在某种断开连接的情况,因为它在单击按钮时不会向控制台输出任何内容 为什么在最新版本的java(当时是152)上,我的代码会给出不可预测的、通常是不想要的结果。我试图给出创建以下场景的最低代码 import javafx.application.Application; import ja

在java
Windows 10 pro x64 jre 1.8.0_60
中,以下代码生成预期输出(单击html按钮后):

但是,在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");
    }
}}
导入javafx.application.application;
导入javafx.beans.value.ChangeListener;
导入javafx.beans.value.observeValue;
导入javafx.concurrent.Worker;
导入javafx.scene.scene;
导入javafx.scene.layout.BorderPane;
导入javafx.scene.web.WebEngine;
导入javafx.scene.web.WebView;
导入javafx.stage.stage;
导入netscape.javascript.JSObject;
公共类主扩展应用程序{
公共静态void main(字符串[]args){
发射(args);
}
@凌驾
公共无效开始(阶段primaryStage){
WebView浏览器=新建WebView();
final-WebEngine-WebEngine=browser.getEngine();
setJavaScriptEnabled(true);
webEngine.load(“https://api.ipify.org/?format=json");
webEngine.getLoadWorker().stateProperty().addListener(
新的ChangeListener(){
@凌驾
公共无效已更改(observeValue ov,Worker.State oldState,Worker.State newState){
if(newState==Worker.State.successed){
jso=(JSObject)webEngine.executeScript(“窗口”);
webEngine.executeScript(
“var button=document.createElement(\“button\”);\n”+
“button.innerHTML=\“做点什么”;\n”+
“var body=document.getElementsByTagName(\“body\”)[0];\n”+
“body.appendChild(按钮);\n”+
“button.addEventListener(\”单击“”,函数(){java.exit();});”;
setMember(“java”,newbridge());
}
}
});
边框窗格面板=新边框窗格(浏览器);
场景=新场景(面板,700700);
初级阶段。场景(场景);
primaryStage.show();
}
公共级桥梁{
公共无效出口(){
System.out.println(“你好世界”);
}
}}

您正在将
new Bridge()
传递给setMember方法。因为没有变量保存桥实例,所以在按下按钮之前它会被垃圾收集

从:

注意,在上面的示例中,应用程序持有对
JavaApplication
实例的引用。这是JavaScript回调执行所需方法所必需的

在以下示例中,应用程序不包含对Java对象的引用:

jsobjectwindow=(JSObject)webEngine.executeScript(“window”)
window.setMember(“app”,newjavaapplication())

在这种情况下,由于属性值是本地对象,
“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);
        }
    }
new ChangeListener(){
专用最终桥=新桥();
@凌驾
公共无效已更改(observeValue ov,Worker.State oldState,Worker.State newState){
if(newState==Worker.State.successed){
// ...
setMember(“java”,桥);
}
}

为什么在早期版本的Java中没有出现这种情况?因为不同的Java版本可以自由更改垃圾收集的时间和行为。你很幸运,但在以后的版本中,你的运气就没有了。

因此
成为新的匿名ChangeListener实例的类属性。ObservalEvalue
addListener方法持有对新的ChangeListener实例的强引用,以防止它被垃圾收集,从而防止桥实例被垃圾收集?这大约涵盖了它,是的。
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);
        }
    }