5

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");
  }
}

3 Answers 3

5

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks you! Your answer helped me solve 2 of my problems at once :)
4

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");
        }
    }
}

4 Comments

Making it public is the important thing, whether it is an inner class, or a top-level class defined in its own source file.
Oh yes, I could have seen it myself. Thank you, I am coming from a language where a class not defined as public is public by default. Can you suggest me an editor that gives me an overview of my classes' signatures? :)
Doesn't work for me on JavaFX 13. It shows "READY" but not "hello".
Any idea why this is? Thank you btw
2

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());
        }
    }

}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.