我的目标是从Web服务器加载文档,然后解析其DOM以获取特定内容。加载DOM是我的问题。
我正在尝试使用javafx.scene.web.WebEngine
,因为这似乎应该能够执行所有必要的机制,包括javascript执行,这可能会影响最终的DOM。
加载文档时,它似乎陷入RUNNING
状态并且永远不会达到SUCCEEDED
状态,我认为在从WebEngine.getDocument()
访问DOM之前需要该状态。
无论是从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);
}
}
答案 0 :(得分:1)
对于Worker
的{{1}}属性的更改将在FX应用程序线程上发生,即使该工作程序在后台线程上运行。 (JavaFX属性本质上是单线程的。)在加载Web引擎内容的线程的实现中,有一个调用state
来改变worker的状态。
由于您的任务阻止,直到工作人员的状态发生变化,并且由于您在FX应用程序线程上运行任务,因此您基本上已经使FX应用程序线程死锁:直到负载工作者状态的更改才会发生。您的任务完成(因为它在同一个线程上运行),并且您的任务无法完成,直到状态发生变化(这就是您编写任务的目的)。
阻止FX应用程序线程基本上总是错误的。相反,您应该阻止另一个线程,直到您想要的条件为真(创建Web引擎并加载线程完成),然后执行下一个要发生的事情(如果需要,再次使用Platform.runLater(...)
在FX应用程序线程上执行。
以下是我认为您正在尝试做的事情的示例:
Platform.runLater(...)