loadContent
load
reload()
会补偿的。
show()
在加载内容之前。由于内容是异步加载的,因此完全有可能在调用之后的语句之前加载
负载
或
饰面。
然后,解决方法是将内容放在一个文件中,并调用WebEngines
reload()
Path htmlFile = Files.createTempFile("snapshot-", ".html");
Files.writeString(htmlFile, html);
WebEngine engine = myWebView.getEngine();
engine.getLoadWorker().stateProperty().addListener(
new ChangeListener<Worker.State>() {
private boolean reloaded;
@Override
public void changed(ObservableValue<? extends Worker.State> obs,
Worker.State oldState,
Worker.State newState) {
if (reloaded) {
Image image = myWebView.snapshot(null, null);
doStuffWithImage(image);
try {
Files.delete(htmlFile);
} catch (IOException e) {
log.log(Level.WARN, "Couldn't delete " + htmlFile, e);
}
} else {
reloaded = true;
engine.reload();
}
}
});
engine.load(htmlFile.toUri().toString());
但因为你在使用
static
对于所有内容,都必须添加一些字段:
private static boolean reloaded;
private static volatile Path htmlFile;
/** Listens for a SUCCEEDED state to activate image capture **/
private static ChangeListener<Worker.State> stateListener = (ov, oldState, newState) -> {
if (newState == Worker.State.SUCCEEDED) {
if (reloaded) {
WritableImage snapshot = webView.snapshot(new SnapshotParameters(), null);
capture.set(SwingFXUtils.fromFXImage(snapshot, null));
finished.set(true);
stage.hide();
try {
Files.delete(htmlFile);
} catch (IOException e) {
log.log(Level.WARN, "Couldn't delete " + htmlFile, e);
}
} else {
reloaded = true;
webView.getEngine().reload();
}
}
};
然后每次加载内容时都必须重置它:
Path htmlFile = Files.createTempFile("snapshot-", ".html");
Files.writeString(htmlFile, html);
Platform.runLater(new Thread(() -> {
try {
reloaded = false;
stage.show(); // JDK-8087569: will not capture without showing stage
stage.toBack();
webView.getEngine().load(htmlFile);
}
catch(Throwable t) {
thrown.set(t);
}
}));
注意,有更好的方法来执行多线程处理。您可以简单地使用
volatile
领域:
private static volatile boolean started;
private static volatile boolean finished = true;
private static volatile Throwable thrown;
private static volatile BufferedImage capture;
(布尔字段默认为false,对象字段默认为null。与C程序不同,这是Java的硬保证;没有未初始化的内存。)
与其在循环中轮询另一个线程中所做的更改,不如使用同步、锁或更高级别的类,如
CountDownLatch
它在内部使用这些东西:
private static final CountDownLatch initialized = new CountDownLatch(1);
private static volatile CountDownLatch finished;
private static volatile BufferedImage capture;
private static volatile Throwable thrown;
private static boolean reloaded;
private static volatile Path htmlFile;
// main javafx objects
private static WebView webView = null;
private static Stage stage = null;
private static ChangeListener<Worker.State> stateListener = (ov, oldState, newState) -> {
if (newState == Worker.State.SUCCEEDED) {
if (reloaded) {
WritableImage snapshot = webView.snapshot(null, null);
capture = SwingFXUtils.fromFXImage(snapshot, null);
finished.countDown();
stage.hide();
try {
Files.delete(htmlFile);
} catch (IOException e) {
log.log(Level.WARNING, "Could not delete " + htmlFile, e);
}
} else {
reloaded = true;
webView.getEngine().reload();
}
}
};
@Override
public void start(Stage primaryStage) {
log.fine("Started JavaFX, creating WebView...");
stage = primaryStage;
primaryStage.setScene(new Scene(webView = new WebView()));
Worker<Void> worker = webView.getEngine().getLoadWorker();
worker.stateProperty().addListener(stateListener);
webView.getEngine().setOnError(e -> {
thrown = e.getException();
});
// Prevents JavaFX from shutting down when hiding window, useful for calling capture(...) in succession
Platform.setImplicitExit(false);
initialized.countDown();
}
public static BufferedImage capture(String html)
throws InterruptedException,
IOException {
htmlFile = Files.createTempFile("snapshot-", ".html");
Files.writeString(htmlFile, html);
if (initialized.getCount() > 0) {
new Thread(() -> Application.launch(SnapshotRaceCondition2.class)).start();
initialized.await();
}
finished = new CountDownLatch(1);
thrown = null;
Platform.runLater(() -> {
reloaded = false;
stage.show(); // JDK-8087569: will not capture without showing stage
stage.toBack();
webView.getEngine().load(htmlFile.toUri().toString());
});
finished.await();
if (thrown != null) {
throw new IOException(thrown);
}
return capture;
}
reloaded
未声明为volatile,因为它仅在JavaFX应用程序线程中访问。