【问题标题】:javaFX webview window.onload is fired before loadworker succeedsjavaFX webview window.onload 在 loadworker 成功之前被触发
【发布时间】:2014-08-20 12:32:32
【问题描述】:

我在我的应用程序中使用 JavaFX webview。使用以下代码,我在页​​面加载后设置了一个成员

webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() {
    @Override
    public void changed(ObservableValue ov, Worker.State oldState, Worker.State newState) {
        if (newState == Worker.State.SUCCEEDED) {
            JSObject window = (JSObject) webEngine.executeScript("window");
            window.setMember("mymember", new JavaScriptBridge(this));
        }
    }
});

现在在 javascript 中我可以调用 mymember.doSomething() 例如当我按下按钮并成功执行时调用,但是如果我将以下代码添加到 html

<script>
function startup() {
    mymember.doSomething();
}
window.onload=startup;
</script>

页面加载时不会自动执行。似乎window.onload 是在LoadWorker 得到通知之前执行的。所以mymember 还没有设置。但是另一方面,我不能在加载 html 之前设置mymember,对吧?

知道什么时候我需要设置mymember 以便在执行window.onload 时准备好它?

谢谢!

【问题讨论】:

    标签: webview javafx onload


    【解决方案1】:

    也许现在回答这个问题为时已晚,但在回答了this 的问题后,我一直在努力寻找在网页完全加载后必须调用executeScript 的原因。

    所以我做了这个测试:

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

    在单击按钮之前不会加载内容,但我们已经在浏览器上创建了 JavaScript 对象。

    在单击按钮之前,输出控制台上没有任何内容。但是如果我们点击按钮......这是输出:

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

    正如我们所见,Java 对象在脚本执行之前被有效地传递给脚本,并且在加载内容的同时成功调用了app.callJavascript

    请注意,为了访问加载的 DOM 的共同目的,在 State.SUCCEEDED 之后调用 executeScript 的常用方法仍然是推荐的方式。

    【讨论】:

    • 这仅适用于加载的第一页。该桥不适用于后续页面。
    【解决方案2】:

    所有(包括后续)页面的炒锅: JAVA代码中设置“java”的地方:

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

    HTML(后续)页面,如果没有设置 var "java" 则等待 100 毫秒(由 JAVA 外部设置):

     <script>                   
            function init(){
                if (typeof java !== 'undefined') {
                    java.doSomething();
                }else{
                    setTimeout(function(){ init() }, 100 );
                }
            }
    
            $(document).ready(function(){           
                init();         
            });
        </script> 
    

    【讨论】:

      【解决方案3】:

      是的,loadworker 总是在window.onloaddocument.onload 之后执行。

      您可以尝试的解决方法,您可以在javascript中创建新的侦听器,例如所谓的:

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

      然后在你的 loadworker 中,你可以这样做:

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

      如您所见,您在setMember 之后执行此webview.getEngine().executeScript("const event = new Event('deviceready');document.dispatchEvent(event);");,因此您可以在自定义事件侦听器deviceready 中进行初始化,而不是在window.onload 中初始化您的工作,这样您就可以更好地控制页面加载和java端loadworker的顺序。

      这正是cordova的做法,这个想法就是由此而来。

      JQuery document.ready vs Phonegap deviceready

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多