user3033490
user3033490

Reputation:

JavaFx WebView wait for Java method to finish

I am calling a Java method from JavaScript in a JavaFx WebView. The Java method does some work, i.e. does not return immediately. What is a natural way to wait for the method to finish from JavaScript?

Upvotes: 0

Views: 1079

Answers (1)

James_D
James_D

Reputation: 209408

Since everything is executed on the FX Application Thread, the Java method you call needs to run the long-running process on a background thread (otherwise you will make the UI unresponsive). You can pass the method a Javascript function to call when it is complete: note that this javascript function needs to be called on the FX Application Thread. One way is to wrap the call in Platform.runLater(), but using a Task makes things a bit cleaner.

Here is a SSCCE:

import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import netscape.javascript.JSObject;

public class WebViewCallbackTest extends Application {

    private static final String HTML = 
              "<html>"
            + "  <head>"
            + "    <script>"
            + ""
            + "      function doCall() {"
            + "         javaApp.doLongRunningCall('updateResults');"
            + "      }"
            + ""
            + "      function updateResults(results) {"
            + "         document.getElementById('results').innerHTML = results ;"
            + "      }"
            + ""
            + "    </script>"
            + "  </head>"
            + "  <body>"
            + "    <div>"
            + "      Result of call:"
            + "    </div>"
            + "    <div id='results'></div>"
            + "  </body>"
            + "</html>";

    private Button button;

    private JSObject window;

    @Override
    public void start(Stage primaryStage) {
        WebView webView = new WebView();
        webView.getEngine().loadContent(HTML);

        BorderPane root = new BorderPane(webView);

        window = (JSObject) webView.getEngine().executeScript("window");
        window.setMember("javaApp", this);


        button = new Button("Run process");
        button.setOnAction(e -> webView.getEngine().executeScript("doCall()"));

        HBox controls = new HBox(button);
        controls.setAlignment(Pos.CENTER);
        controls.setPadding(new Insets(5));
        root.setBottom(controls);

        Scene scene = new Scene(root, 600, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public void doLongRunningCall(String callback) {
        Task<String> task = new Task<String>() {
            @Override
            public String call() throws InterruptedException {
                Thread.sleep(2000);
                return "The answer is 42";
            }
        };

        task.setOnSucceeded(e -> 
            window.call(callback, task.getValue()));
        task.setOnFailed(e -> 
            window.call(callback, "An error occurred"));

        button.disableProperty().bind(task.runningProperty());

        new Thread(task).start();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

(There may be an easier way than this: I am not an expert on Webview Javascript <-> Java communication, but this seems OK to me.)

Upvotes: 2

Related Questions