私の目的は、Webサーバーからドキュメントを読み込んで、特定のコンテンツのDOMを解析することです。 DOMのロードは私の問題です。なぜWebEngineのワーカースレッドは完了しませんか?
javafx.scene.web.WebEngine
を使用しようとしていますが、最終的なDOMに影響を与える可能性のあるjavascriptの実行を含む必要なすべての仕組みを行うことができるように見えます。
ドキュメントをロードするときにRUNNING
状態になっていて、WebEngine.getDocument()
からDOMにアクセスする前に、SUCCEEDED
状態には達していないようです。
これは、URLまたはリテラルコンテンツ(この最小限の例で使用されている)からロードする場合に発生します。
誰かが私が間違っている、または誤解していることを見ることができますか?
ご協力いただきありがとうございます。
import java.util.concurrent.ExecutionException;
import org.w3c.dom.Document;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.concurrent.Worker;
import javafx.embed.swing.JFXPanel;
import javafx.scene.web.WebEngine;
public class WebEngineProblem {
private static Task<WebEngine> getEngineTask() {
Task<WebEngine> task = new Task<>() {
@Override
protected WebEngine call() throws Exception {
WebEngine webEngine = new WebEngine();
final Worker<Void> loadWorker = webEngine.getLoadWorker();
loadWorker.stateProperty().addListener((obs, oldValue, newValue) -> {
System.out.println("state:" + newValue);
if (newValue == State.SUCCEEDED) {
System.out.println("finished loading");
}
});
webEngine.loadContent("<!DOCTYPE html>\r\n" + "<html>\r\n" + "<head>\r\n" + "<meta charset=\"UTF-8\">\r\n"
+ "<title>Content Title</title>\r\n" + "</head>\r\n" + "<body>\r\n" + "<p>Body</p>\r\n" + "</body>\r\n"
+ "</html>\r\n");
State priorState = State.CANCELLED; //should never be CANCELLED
double priorWork = Double.NaN;
while (loadWorker.isRunning()) {
final double workDone = loadWorker.getWorkDone();
if (loadWorker.getState() != priorState || priorWork != workDone) {
priorState = loadWorker.stateProperty().getValue();
priorWork = workDone;
System.out.println(priorState + " " + priorWork + "/" + loadWorker.getTotalWork());
}
Thread.sleep(1000);
}
return webEngine;
}
};
return task;
}
public static void main(String[] args) {
new JFXPanel(); // Initialise the JavaFx Platform
WebEngine engine = null;
Task<WebEngine> task = getEngineTask();
try {
Platform.runLater(task);
Thread.sleep(1000);
engine = task.get(); // Never completes as always RUNNING
}
catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
// This code is never reached as the content never completes loading
// It would fail as it's not on the FX thread.
Document doc = engine.getDocument();
String content = doc.getTextContent();
System.out.println(content);
}
}
タスクがバックグラウンドスレッドで実行するように設計されています:あなたは、FXアプリケーションのスレッドでこのタスクを実行している
はここでの例では、私はあなたがやろうとしていると思う何をしています。 'SUCCEEDED'への状態変更は、FXアプリケーションスレッドでも発生しなければならないので、タスクが完了するまで状態を変更することはできません。あなたのwhileループは、 'loadWorker'が' RUNNING'ステートから抜けるまであなたが事実上デッドロックの奇妙な形を持つまで完了しません。 –
確かに、 'Platform.runLater() '呼び出しはFXApplicationスレッド上でタスクを強制的に実行する方法ですが、ワーカースレッドは別々のものですか? webEngine.loadContent()はすぐに戻ります。そのため、ロードは別のワーカースレッドで発生している必要があります。 "バックグラウンドスレッドで常にローディングが発生するバックグラウンドジョブのスケジューリングの直後にロードを開始するメソッドが返される進行状況を追跡したり、ジョブを取り消したりするには、getLoadWorker()メソッドで使用可能なWorkerインスタンスを使用する。 https://docs.oracle.com/javase/8/javafx/api/javafx/scene/web/WebEngine.html – Dragonthoughts
ワーカースレッドは別々のものですが、ワーカーの 'stateProperty() 'に対する実際の変更は、 'FXアプリケーションスレッドで発生する必要があります。 (基本的にこれらのプロパティはすべてシングルスレッドです。)したがって、ワーカースレッドの実装では、どこかに 'Platform.runLater(...) '状態を更新します。 FXアプリケーションスレッドをブロックした場合、その呼び出しは実際には起こりません。 (基本的に、「ヘッドレス」モードで実行していても、FXアプリケーションスレッドを決してブロックしないでください。) –