JavaFX เอกสารรัฐที่WebView
พร้อมเมื่อWorker.State.SUCCEEDED
ถึงแต่ถ้าคุณรอในขณะที่ (เช่นAnimation
, Transition
, PauseTransition
ฯลฯ ) หน้าว่างที่มีการแสดง
สิ่งนี้ชี้ให้เห็นว่ามีเหตุการณ์ที่เกิดขึ้นภายใน WebView ที่พร้อมสำหรับการดักจับ แต่มันคืออะไร
มีตัวอย่างโค้ดมากกว่า 7,000 รายการใน GitHub ที่ใช้SwingFXUtils.fromFXImage
แต่ส่วนใหญ่ดูเหมือนว่าไม่เกี่ยวข้องกับWebView
มันเป็นแบบอินเทอร์แอคทีฟ (มาสก์มนุษย์ที่มีสภาพการแข่งขัน) หรือใช้การเปลี่ยนแปลงตามอำเภอใจ
ฉันได้พยายาม:
ฟัง
changed(...)
จากภายในส่วนWebView
ข้อมูล (DoubleProperty
ใช้คุณสมบัติความสูงและความกว้างObservableValue
ซึ่งสามารถตรวจสอบสิ่งเหล่านี้ได้)- v ไม่สามารถทำงานได้ บางครั้งค่าดูเหมือนจะเปลี่ยนจากรูทีนการระบายสีซึ่งนำไปสู่เนื้อหาบางส่วน
บอกอะไร ๆ ทุกอย่างไป
runLater(...)
ที่ FX Application Thread- techniques เทคนิคหลายอย่างใช้สิ่งนี้ แต่การทดสอบหน่วยของฉันเอง (เช่นเดียวกับข้อเสนอแนะที่ยอดเยี่ยมจากนักพัฒนาอื่น ๆ ) อธิบายว่าเหตุการณ์มักจะอยู่ในเธรดที่ถูกต้องและการโทรนี้ซ้ำซ้อน สิ่งที่ดีที่สุดที่ฉันสามารถนึกได้ก็คือการเพิ่มความล่าช้าเพียงพอผ่านการเข้าคิวที่ใช้งานได้สำหรับบางคน
การเพิ่มผู้ฟัง / ทริกเกอร์ DOM หรือทริกเกอร์ / ทริกเกอร์ JavaScript ให้กับ
WebView
- JavaScript ทั้ง JavaScript และ DOM ดูเหมือนจะโหลดอย่างถูกต้องเมื่อ
SUCCEEDED
มีการเรียกแม้จะมีการจับภาพเปล่า ฟัง DOM / JavaScript ดูเหมือนจะไม่ช่วย
- JavaScript ทั้ง JavaScript และ DOM ดูเหมือนจะโหลดอย่างถูกต้องเมื่อ
การใช้
Animation
หรือTransition
เพื่อ "สลีป" อย่างมีประสิทธิภาพโดยไม่ปิดกั้นเธรด FX หลัก- approach วิธีการนี้ใช้งานได้และหากการหน่วงเวลานั้นนานพอสามารถสร้างผลการทดสอบหน่วยได้ถึง 100% แต่เวลาการเปลี่ยนภาพนั้นดูเหมือนจะเป็นช่วงเวลาในอนาคตที่เราคาดเดาและออกแบบไม่ดี สำหรับแอพพลิเคชั่นที่มีประสิทธิภาพหรือมีภารกิจสำคัญบังคับให้โปรแกรมเมอร์ทำการแลกเปลี่ยนระหว่างความเร็วหรือความน่าเชื่อถือซึ่งเป็นประสบการณ์ที่ไม่ดีสำหรับผู้ใช้
เวลาไหนดีในการโทรWebView.snapshot(...)
?
การใช้งาน:
SnapshotRaceCondition.initialize();
BufferedImage bufferedImage = SnapshotRaceCondition.capture("<html style='background-color: red;'><h1>TEST</h1></html>");
/**
* Notes:
* - The color is to observe the otherwise non-obvious cropping that occurs
* with some techniques, such as `setPrefWidth`, `autosize`, etc.
* - Call this function in a loop and then display/write `BufferedImage` to
* to see strange behavior on subsequent calls.
* - Recommended, modify `<h1>TEST</h1` with a counter to see content from
* previous captures render much later.
*/
ตัวอย่างโค้ด:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.image.WritableImage;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Logger;
public class SnapshotRaceCondition extends Application {
private static final Logger log = Logger.getLogger(SnapshotRaceCondition.class.getName());
// self reference
private static SnapshotRaceCondition instance = null;
// concurrent-safe containers for flags/exceptions/image data
private static AtomicBoolean started = new AtomicBoolean(false);
private static AtomicBoolean finished = new AtomicBoolean(true);
private static AtomicReference<Throwable> thrown = new AtomicReference<>(null);
private static AtomicReference<BufferedImage> capture = new AtomicReference<>(null);
// main javafx objects
private static WebView webView = null;
private static Stage stage = null;
// frequency for checking fx is started
private static final int STARTUP_TIMEOUT= 10; // seconds
private static final int STARTUP_SLEEP_INTERVAL = 250; // millis
// frequency for checking capture has occured
private static final int CAPTURE_SLEEP_INTERVAL = 10; // millis
/** Called by JavaFX thread */
public SnapshotRaceCondition() {
instance = this;
}
/** Starts JavaFX thread if not already running */
public static synchronized void initialize() throws IOException {
if (instance == null) {
new Thread(() -> Application.launch(SnapshotRaceCondition.class)).start();
}
for(int i = 0; i < (STARTUP_TIMEOUT * 1000); i += STARTUP_SLEEP_INTERVAL) {
if (started.get()) { break; }
log.fine("Waiting for JavaFX...");
try { Thread.sleep(STARTUP_SLEEP_INTERVAL); } catch(Exception ignore) {}
}
if (!started.get()) {
throw new IOException("JavaFX did not start");
}
}
@Override
public void start(Stage primaryStage) {
started.set(true);
log.fine("Started JavaFX, creating WebView...");
stage = primaryStage;
primaryStage.setScene(new Scene(webView = new WebView()));
// Add listener for SUCCEEDED
Worker<Void> worker = webView.getEngine().getLoadWorker();
worker.stateProperty().addListener(stateListener);
// Prevents JavaFX from shutting down when hiding window, useful for calling capture(...) in succession
Platform.setImplicitExit(false);
}
/** Listens for a SUCCEEDED state to activate image capture **/
private static ChangeListener<Worker.State> stateListener = (ov, oldState, newState) -> {
if (newState == Worker.State.SUCCEEDED) {
WritableImage snapshot = webView.snapshot(new SnapshotParameters(), null);
capture.set(SwingFXUtils.fromFXImage(snapshot, null));
finished.set(true);
stage.hide();
}
};
/** Listen for failures **/
private static ChangeListener<Throwable> exceptListener = new ChangeListener<Throwable>() {
@Override
public void changed(ObservableValue<? extends Throwable> obs, Throwable oldExc, Throwable newExc) {
if (newExc != null) { thrown.set(newExc); }
}
};
/** Loads the specified HTML, triggering stateListener above **/
public static synchronized BufferedImage capture(final String html) throws Throwable {
capture.set(null);
thrown.set(null);
finished.set(false);
// run these actions on the JavaFX thread
Platform.runLater(new Thread(() -> {
try {
webView.getEngine().loadContent(html, "text/html");
stage.show(); // JDK-8087569: will not capture without showing stage
stage.toBack();
}
catch(Throwable t) {
thrown.set(t);
}
}));
// wait for capture to complete by monitoring our own finished flag
while(!finished.get() && thrown.get() == null) {
log.fine("Waiting on capture...");
try {
Thread.sleep(CAPTURE_SLEEP_INTERVAL);
}
catch(InterruptedException e) {
log.warning(e.getLocalizedMessage());
}
}
if (thrown.get() != null) {
throw thrown.get();
}
return capture.get();
}
}
ที่เกี่ยวข้อง:
- สกรีนช็อตของเว็บเพจเต็มโหลดลงในส่วนประกอบ JavaFX WebView ไม่เพียง แต่ส่วนที่มองเห็นได้
- ฉันสามารถถ่ายภาพทิวทัศน์โดยทางโปรแกรมได้หรือไม่?
- ภาพหน้าจอทั้งหน้า, Java
- JavaFX 2.0+ WebView / WebEngine แสดงผลหน้าเว็บเป็นรูปภาพ
- ตั้งค่าความสูงและความกว้างของเวทีและฉากเป็น javafx
- JavaFX: วิธีการปรับขนาดเวทีเมื่อใช้เว็บวิว
- แก้ไขการปรับขนาดของ Webview ที่ฝังใน Tabelcell
- https://docs.oracle.com/javase/8/javafx/embedded-browser-tutorial/add-browser.htm#CEGDIBBI
- http://docs.oracle.com/javafx/2/swing/swing-fx-interoperability.htm#CHDIEEJE
- https://bugs.openjdk.java.net/browse/JDK-8126854
- https://bugs.openjdk.java.net/browse/JDK-8087569
Platform.runLater
ได้รับการทดสอบและไม่สามารถแก้ไขได้ โปรดลองด้วยตัวคุณเองถ้าคุณไม่เห็นด้วย ฉันยินดีที่จะผิดมันจะปิดปัญหา
SUCCEEDED
รัฐ (ซึ่งผู้ฟังยิงบนเธรด FX) เป็นเทคนิคที่เหมาะสม หากมีวิธีแสดงเหตุการณ์ที่อยู่ในคิวฉันจะได้ลองดู ฉันได้พบข้อเสนอแนะกระจัดกระจายผ่านความคิดเห็นในฟอรัม Oracle และคำถาม SO ที่WebView
ต้องทำงานในเธรดของตัวเองโดยการออกแบบดังนั้นหลังจากวันที่ทดสอบฉันมุ่งเน้นพลังงานที่นั่น หากข้อสันนิษฐานนั้นผิด ฉันเปิดให้คำแนะนำที่สมเหตุสมผลที่แก้ไขปัญหาโดยไม่ต้องรอเวลาโดยพลการ
loadContent
วิธีการหรือเมื่อโหลดไฟล์ URL เท่านั้น