JavaFX WebEngine JavaScript 调用
JavaFX WebEngine JavaScript Upcalls
在 java Windows 10 pro x64 jre 1.8.0_60
中,以下代码生成预期的输出(单击 html 按钮后):
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 实例,因此在您按下按钮之前它会被垃圾回收。
Note that in the above example, the application holds a reference to the JavaApplication
instance. This is required for the callback from JavaScript to execute the desired method.
In the following example, the application does not hold a reference to the Java object:
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("app", new JavaApplication());
In this case, since the property value is a local object, "new JavaApplication()"
, the value may be garbage collected in next GC cycle.
When a user clicks the link, it does not guarantee to execute the callback method exit
.
将您的 Bridge 对象保存在一个字段中以防止对其进行垃圾回收:
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 版本可以自由改变垃圾收集的时间和行为。你很幸运,但在以后的版本中,你的运气 运行 消失了。
在 java Windows 10 pro x64 jre 1.8.0_60
中,以下代码生成预期的输出(单击 html 按钮后):
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 实例,因此在您按下按钮之前它会被垃圾回收。
Note that in the above example, the application holds a reference to the
JavaApplication
instance. This is required for the callback from JavaScript to execute the desired method.In the following example, the application does not hold a reference to the Java object:
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("app", new JavaApplication());
In this case, since the property value is a local object,
"new JavaApplication()"
, the value may be garbage collected in next GC cycle.When a user clicks the link, it does not guarantee to execute the callback method
exit
.
将您的 Bridge 对象保存在一个字段中以防止对其进行垃圾回收:
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 版本可以自由改变垃圾收集的时间和行为。你很幸运,但在以后的版本中,你的运气 运行 消失了。