Reputation: 169
I have a JavaFX WebView and want to call the method "hello" of the class "JavaBridge" from "test.html" displayed in the webview. Why doesn't this work? I making sure that the "bridge" object only be added to the window.object when the page has been fully rendered, so that is probably not the problem. I can't see any problem with the HTML either.
Here is the HTML code ("test.html"):
<html>
<head>
</head>
<body>
<a href="#click" onclick="bridge.hello()">call java</a>
</body>
</html>
And here is the Java Code:
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.concurrent.Worker.State;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import netscape.javascript.JSObject;
public class HelloWorld extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
java.net.URI uri = java.nio.file.Paths.get("test.html").toAbsolutePath().toUri();
WebView root = new javafx.scene.web.WebView();
root.getEngine().load(uri.toString());
root.getEngine().
getLoadWorker().
stateProperty().
addListener(new ChangeListener < State > () {
@Override public void changed(ObservableValue ov, State oldState, State newState) {
if (newState == Worker.State.SUCCEEDED) {
System.out.println("READY");
JSObject jsobj = (JSObject) root.getEngine().executeScript("window");
jsobj.setMember("bridge", new JavaBridge());
}
}
});
primaryStage.setScene(new javafx.scene.Scene(root, 800, 600));
primaryStage.show();
}
}
class JavaBridge {
public void hello() {
System.out.println("hello");
}
}
Upvotes: 5
Views: 3901
Reputation: 115
I had the same problem and the only way to fix it was storing the Bridge on a static variable. this is an example to use the javafx FileChooser.
public class Controller implements Initializable {
@FXML private WebView webview;
@FXML private JFXButton btn_insertimg;
@FXML private AnchorPane anchorpane;
private WebEngine webEngine;
public static Bridge bridge; //it's important to be static
@Override
public void initialize(URL location, ResourceBundle resources) {
webEngine = webview.getEngine();
webEngine.getLoadWorker().stateProperty().addListener(
(ov, oldState, newState) -> {
if (newState == Worker.State.SUCCEEDED) {
//todo when the document is fully loaded
FileChooser fileChooser=new FileChooser();
bridge=new Bridge(webview,fileChooser);
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("myFileChooser", bridge);
System.out.println("member "+window.getMember("myFileChooser").toString());;
}//end of SUCCEEDED state
});
webEngine.load(getClass().getResource("/patient/texteditor/summernote.html").toExternalForm());
}
public class Bridge{
FileChooser fileChooser;
WebView webView;
Bridge(WebView webView,FileChooser fileChooser){
this.webView=webView;
this.fileChooser=fileChooser;
}
public void display(){
fileChooser.showOpenDialog(webView.getScene().getWindow());
}
}
}
Upvotes: 2
Reputation: 51
When using this bridge feature on Java 10.0.2, I noticed that it was not working consistently. Javascript upcalls wasn't working all the times.
After researching, I found out this OpenJDK bug related to Java Garbage Collector, which seems to happen on regular JDK as well: https://bugs.openjdk.java.net/browse/JDK-8170085
Indeed, according to https://docs.oracle.com/javase/9/docs/api/javafx/scene/web/WebEngine.html, it's recommended to store the bridge into a variable to avoid Java GC to collect the object.
After adding a private variable to the class, the JS to Java calls started to work all the time in my Application.
Upvotes: 5
Reputation: 18415
Your inner class should be inside the main class. And it should be public. Like this:
import java.net.URL;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.concurrent.Worker.State;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import netscape.javascript.JSObject;
public class HelloWorld extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
final URL url = getClass().getResource("test.html");
WebView root = new javafx.scene.web.WebView();
root.getEngine().load(url.toExternalForm());
root.getEngine().getLoadWorker().stateProperty().addListener(new ChangeListener<State>() {
@Override
public void changed(ObservableValue ov, State oldState, State newState) {
if (newState == Worker.State.SUCCEEDED) {
System.out.println("READY");
JSObject jsobj = (JSObject) root.getEngine().executeScript("window");
jsobj.setMember("bridge", new JavaBridge());
}
}
});
primaryStage.setScene(new javafx.scene.Scene(root, 800, 600));
primaryStage.show();
}
public class JavaBridge {
public void hello() {
System.out.println("hello");
}
}
}
Upvotes: 4