WebEngine文档:

加载始终发生在后台线程上。启动方法
安排后台作业后立即加载返回。追踪
进度和/或取消作业,请使用可从以下位置获得的Worker实例
getLoadWorker()方法。

我有一个HTML string,我可以通过WebEngine.loadContent(String)加载到WebView上。该字符串大约为500万个字符。在Platform.runLater()中运行它之后(我必须在JavaFX线程中运行它,否则会出现错误),UI挂起了大约一分钟。

如果我不使用Platform.runLater()运行它,则会得到:

java.lang.IllegalStateException: Not on FX application thread; currentThread = populator
    at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:236)
    at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:423)
    at javafx.scene.web.WebEngine.checkThread(WebEngine.java:1216)
    at javafx.scene.web.WebEngine.loadContent(WebEngine.java:931)
    at javafx.scene.web.WebEngine.loadContent(WebEngine.java:919)
    ...

我无法通过webEngine.getLoadWorker().getProgress()查询webEngine,也无法通过webEngine.getLoadWorker().cancel()取消,因为我不得不再次在挂起的JavaFX线程上运行该引擎...

因此,我必须等到页面加载完毕,然后之前运行的任何Platform.runLater(()->webEngine.getLoadWorker().getProgress())(在网页加载过程中)都将运行,每次给我1.0 ...

我用来查询Worker的代码:
// WebView
wvIn.getEngine().getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() {
    class ProgressThread extends Thread {
        private Worker.State loadWorkerState;

        synchronized Worker.State getLoadWorkerState() {
            return loadWorkerState;
        }

        synchronized void setLoadWorkerState(Worker.State loadWorkerState) {
            this.loadWorkerState = loadWorkerState;
        }

        {
            setDaemon(true);
            setName("LoadingWebpageProgressThread");
        }

        public void run() {
            while (true) {
                try {
                    if (getLoadWorkerState() == Worker.State.RUNNING)
                        // piWv ProgressIndicator (WebView loading)
                        Platform.runLater(() -> piWv.setVisible(true));
                    while (getLoadWorkerState() == Worker.State.RUNNING) {
                        Platform.runLater(() -> {
                            piWv.setProgress(wvIn.getEngine().getLoadWorker().getProgress());
                            // TODO delete
                            System.out.println(wvIn.getEngine().getLoadWorker().getProgress());
                        });
                        Thread.sleep(100);
                    }
                    if (getLoadWorkerState() == Worker.State.SUCCEEDED) {
                        Platform.runLater(() -> piWv.setProgress(1d));
                        Thread.sleep(100);
                        Platform.runLater(() -> {
                            piWv.setVisible(false);
                            piWv.setProgress(0d);
                        });
                    }
                    synchronized (this) {
                        wait();
                    }
                } catch (InterruptedException e) {
                }
            }
        }
    };

    final ProgressThread progressThread = new ProgressThread();
    {
        progressThread.start();
    }

    // executed on JavaFX Thread
    @Override
    public void changed(ObservableValue<? extends State> observable, State oldValue, State newValue) {
        if (newValue == State.SUCCEEDED) {
            JSObject window = (JSObject) wvIn.getEngine().executeScript("window");
            window.setMember("controller", mainController);
            progressThread.setLoadWorkerState(newValue);
            progressThread.interrupt();
        } else if (newValue == State.RUNNING) {
            progressThread.setLoadWorkerState(newValue);
            progressThread.interrupt();
        }
        // TODO delete
        System.out.println(oldValue + "->" + newValue);
    }
});

反正有强制在后台线程中加载吗?

JavaFX线程中到底发生了什么?这是填充WebView的过程吗?

最佳答案

您在问题中显示的HTML文件几乎立即加载到我的计算机上。

我认为问题出在您的代码中。好吧,这很麻烦。请让我指出,它看起来不太好,因为它很容易deadlock,并且使用异常来实现guarded-blocks的形式。

看来您不太了解events的概念。
除非您为了简洁起见以某种方式切断了问题中的代码片段,否则ProgressThread实例是完全没有用的。
您已经有一个 ChangeListener 根据需要调度事件,并在JavaFX Application Thread上执行回调。只是使用它。

indicator.setVisible(false); // Start hidden
engine.getLoadWorker().progressProperty().addListener((observable, oldValue, newValue) -> {
    indicator.setProgress(newValue.doubleValue());
    indicator.setVisible(indicator.getProgress() != 1D);
});

另外,您可以只使用binding的功能:
Worker<Void> worker = engine.getLoadWorker();
indicator.visibleProperty().bind(worker.stateProperty().isEqualTo(Worker.State.RUNNING));
indicator.progressProperty().bind(worker.progressProperty());

对于您的JavaScript代码,建议您听文档事件而不是工作人员状态事件:在您的情况下,它们更有意义,并且在页面实际完成渲染时(而不只是在页面数据下载完成时)将触发它们。
engine.documentProperty().addListener((observable, oldValue, newValue) -> {
    if (newValue == null) {
        return;
    }
    JSObject window = (JSObject)engine.executeScript("window");
    window.setMember("controller", mainController);
});

请注意,在我的计算机上,加载过程中进度始终显示为零。我认为问题出在您的HTML代码中:如果您尝试加载其他资源,例如您正在阅读的网页,进度指示器将提供更加愉悦的体验,在页面内容完成之前会进行多次更新加载中。

关于java - Javafx WebEngine:后台 worker 实际上会发生什么? UI卡在loadContent(大HTML文档)上,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41830701/

10-09 09:33