javaFX webview [removed] is fired before loadworker succeeds

前端 未结 3 725
广开言路
广开言路 2021-01-21 22:14

I use a JavaFX webview in my application. With the following code I set a member after the page has been loaded

webEngine.getLoadWorker().stateProperty().addList         


        
相关标签:
3条回答
  • 2021-01-21 22:25

    Maybe it's too late for an answer to this problem, but after answering this question, I've been trying to find a reason why executeScript has to be called after the complete load of the webpage.

    So I've done this test:

        public class EarlyWebEngineTest extends Application {
    
        @Override
        public void start(Stage stage) {
            final WebView webView = new WebView();
            final WebEngine webEngine = webView.getEngine();
    
            // Early call of executeScript to get a JavaScript object, a proxy for the 
            // Java object to be accessed on the JavaScript environment
            JSObject window = (JSObject) webEngine.executeScript("window");
            window.setMember("app", new JavaApplication());
    
            webEngine.getLoadWorker().stateProperty().addListener((ov,oldState,newState)->{
                if(newState==State.SCHEDULED){
                    System.out.println("state: scheduled");
                } else if(newState==State.RUNNING){
                    System.out.println("state: running");
                } else if(newState==State.SUCCEEDED){
                    System.out.println("state: succeeded");
                }
            });
    
            Button button=new Button("Load Content");
            button.setOnAction(e->webEngine.loadContent("<html>"
                    + " <script>function initialize() {"
                    + " var nameVar = \"This is a JS var\"; " 
                    + " app.callJavascript(nameVar);"
                    + "} </script>"
                    + "    <body onLoad=\"initialize()\">Hi, this is a test!</body>"
                    + "</html>"));
    
            VBox vbox = new VBox(10,button,webView);
            Scene scene = new Scene(vbox,400,300);
    
            stage.setScene(scene);
            stage.show();
    
        }
    
        public class JavaApplication {
    
            public void callJavascript(String msg){
                System.out.println("JS>> "+msg);
            }
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    
    }
    

    The content is not loaded until the button is clicked, but we've already created the JavaScript object on the browser.

    Before clicking the button, there's nothing on the output console. But if we click the button... this is the output:

    state: scheduled
    state: running
    JS>> This is a JS var
    state: succeeded
    

    As we can see, the Java object is effectively passed to the script before the latter is executed, and app.callJavascript is successfully called while the content is being loaded.

    Note that for the common purpose of accessing the loaded DOM, the usual approach of calling executeScript after State.SUCCEEDED is still the recommended way.

    0 讨论(0)
  • 2021-01-21 22:37

    Woks for all (including subsequent) pages: Where "java" is set in JAVA code:

    window.setMember("java", new JavaApplication());
    

    HTML (subsequent) page, keep waiting for 100ms if var "java" is not set (externally by JAVA):

     <script>                   
            function init(){
                if (typeof java !== 'undefined') {
                    java.doSomething();
                }else{
                    setTimeout(function(){ init() }, 100 );
                }
            }
    
            $(document).ready(function(){           
                init();         
            });
        </script> 
    
    0 讨论(0)
  • 2021-01-21 22:47

    Yes, loadworker always execute after window.onload or document.onload.

    The workaround you can try, you can create new listener in javascript, for example so-called:

    document.addEventListener("deviceready", function(){
        MyJavaBridge.executeJavaMethod();
    });
    

    And then in your loadworker, you can do this:

    webview.getEngine().getLoadWorker().stateProperty().addListener((ov, oldState, newState) -> {
                
        if (newState == Worker.State.SUCCEEDED) {
            JSObject window = (JSObject) webview.getEngine().executeScript("window");
            System.out.println("window : " + window);
            window.setMember("MyJavaBridge", javaBridge);
            
            webview.getEngine().executeScript("const event = new Event('deviceready');document.dispatchEvent(event);");
        }
    });
    

    As you can see, you execute this webview.getEngine().executeScript("const event = new Event('deviceready');document.dispatchEvent(event);"); after setMember, so instead of initialise your work in window.onload, you can do it in your custom event listener deviceready, so you can have better control on the sequence of page load and java side loadworker.

    This is exactly how cordova doing, this idea is coming from it.

    JQuery document.ready vs Phonegap deviceready

    0 讨论(0)
提交回复
热议问题