1

I am fairly new to JavaFX and am not very experienced in threading either. The project I am working on uses a WebView for all UI functions but freezes when processing data/running methods in Java. I have read about Platform.runlater() but I am unsure how to apply it to my particular situation.

Here is the code that I currently have (it is an example but replicates the issue that I am having).

Main.java

package application;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {
  @Override
  public void start(Stage stage) throws Exception {
    Parent root = FXMLLoader.load(Main.class.getResource("Main.fxml"));
    Scene scene = new Scene(root);
    stage.setScene(scene);
    stage.show();
  }
  public static void main(String[] args) {
    launch(args);
  }
}

Controller.java

import java.net.URL;
import java.util.ResourceBundle;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker.State;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import netscape.javascript.JSObject;

public class Controller implements Initializable {
    @FXML
    private WebView webView;
    private WebEngine webEngine;
    private AppInterface appInterface;

    private void loadPage(String fileName) {
        URL url = this.getClass().getResource(fileName);
        webEngine = webView.getEngine();
        webEngine.load(url.toExternalForm());
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
            
        loadPage("/resources/index.html");
        appInterface = new AppInterface(webEngine);

        webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<State>() {
            @Override
            public void changed(ObservableValue<? extends State> observableValue, State oldState, State newState) {
                if (newState == State.SUCCEEDED) {
                    JSObject window = (JSObject) webEngine.executeScript("window");
                    window.setMember("javaApp", appInterface);
                }
            }
        });     
    }
}

AppInterface.java

import javafx.scene.web.WebEngine;

public class AppInterface {
    WebEngine webEngine;

    public AppInterface(WebEngine inputWebEngine) {
        webEngine = inputWebEngine;
    }
    
    public void getFibN(int digit) {
        System.out.println("Calculating digit " + digit + " of Fibonacci Sequence.");
        
        int value = Fibonacci.getDigit(digit);
        
        System.out.println("Fibonacci Sequence, digit " + digit + ": " + value);

        webEngine.executeScript("displayWebView('" + value + "')");
    }
}

Fibonacci.java

class Fibonacci {
    static int getDigit(int n)
    {
        if (n <= 1)
            return n;
        return getDigit(n - 1) + getDigit(n - 2);
    }
}

index.html

<!DOCTYPE html>
<html>
    <head>
        <script>
            function getMessage(){
                javaApp.getFibN(47);
            }
            function displayWebView(message){
                document.body.append(message);
            }
        </script>
    </head>
    <body onload="javascript:setTimeout(getMessage, 1000)"> 
</body>
</html>

Main.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.web.*?>
<?import javafx.scene.web.WebView?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane prefHeight="600.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Controller">
  <children>
    <!-- Given the webView ID to initiate the web page -->
    <WebView fx:id="webView" prefWidth="1000.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
  </children>
</AnchorPane>

I have tried creating a new thread in the Fibonacci class but get an error stating that it is not the JavaFX thread. What am I doing wrong to keep the UI from freezing while the task is running?

  • 1
    fib(47) takes 10 seconds to calculate on my machine. You need to use a [Task](https://openjfx.io/javadoc/17/javafx.graphics/javafx/concurrent/Task.html). – jewelsea Apr 11 '22 at 20:32
  • I chose 47 so that it would take long enough to demonstrate the UI locking up. The actual task that I am running takes around a minute but will vary by user settings. Where would I implement a task? Would it be in the controller? All of the examples and questions I have viewed don't include executing JS as a UI update. – SolveEtCoagula07 Apr 11 '22 at 20:37
  • 3
    Treat the JS execution from Java as similar to updating a node in the scene graph from a task, there are examples for that in the documentation I linked. I suggest you simplify your task, by updating a node in the scene graph first, without the added complication of a WebView and add the WebView/JavaScript integration later if you still need it. – jewelsea Apr 11 '22 at 20:57
  • 2
    There are a few examples on this site, as well as the excellent examples in the Javadocs. [Here](https://stackoverflow.com/questions/30249493/using-threads-to-make-database-requests) is one I wrote a few years ago, which accesses a database and returns a result. The basic pattern is the same. (Long-running task returning a value.) – James_D Apr 12 '22 at 01:13

0 Answers0