一、坑场景复现
源于w3cschool的JavaFX WebEngine测试例子,运行代码都没有输出,一直在研究为什么会出现这样的结果。
在测试过程中使用WebEngine的html加载,使用WebEngine的url加载,使用WebView加载url,使用仅Scene装载WebView(没有加入Stage或者没有运行stage.show()方法),还有使用Scene装载WebView(加入Stage并且运行stage.show()方法)等等,其中只有WebEngine的html加载、Scene装载WebView(加入Stage并且运行stage.show()方法)有输出文档信息,其余都没有输出结果。
(1)代码对比
1、WebEngine 加载html
import com.sun.javafx.application.PlatformImpl;
import javafx.concurrent.Worker;
import javafx.scene.web.WebEngine;public class A {public static void main(String[] args) throws InterruptedException {PlatformImpl.startup(()->{doDom();System.err.println("finish");});}public static void doDom() {WebEngine webEngine = new WebEngine();webEngine.getLoadWorker().stateProperty().addListener((obs, oldValue, newValue) -> {if (newValue == Worker.State.SUCCEEDED) {String html = (String) webEngine.executeScript("document.documentElement.outerHTML");System.out.println(html);System.exit(0);}System.out.println(newValue + " ");}); // addListener()String s="<html>\n" +"\n" +"<head>\n" +"<title>我的第一个 HTML 页面</title>\n" +"</head>\n" +"\n" +"<body>\n" +"<p>body 元素的内容会显示在浏览器中。</p>\n" +"<p id='a'>title 元素的内容会显示在浏览器的标题栏中。</p>\n" +"</body>\n" +"<script>var a =document.getElementById('a');\n" +"a.style.color='red';</script>\n" +"</html>\n";webEngine.loadContent(s);}
}
2、WebEngine 加载url
import com.sun.javafx.application.PlatformImpl;
import javafx.concurrent.Worker.State;
import javafx.scene.web.WebEngine;public class Test {public static void main(String[] args) throws InterruptedException {PlatformImpl.startup(() -> {doDom();System.err.println("finish");});}public static void doDom(){WebEngine webEngine = new WebEngine();webEngine.getLoadWorker().stateProperty().addListener((obs, oldValue, newValue) -> {if (newValue == State.SUCCEEDED) {String html = (String) webEngine.executeScript("document.documentElement.outerHTML");System.out.println(html);System.exit(0);}System.out.println(newValue + " ");}); // addListener()webEngine.load("https://www.w3cschool.cn/java/javafx-webengine.html");}
}
(2)输出结果
1、WebEngine 加载html:
SCHEDULED
RUNNING
finish
<html><head>
<title>我的第一个 HTML 页面</title>
</head><body>
<p>body 元素的内容会显示在浏览器中。</p>
<p id="a" style="color: red;">title 元素的内容会显示在浏览器的标题栏中。</p><script>var a =document.getElementById('a');
a.style.color='red';</script></body></html>Process finished with exit code 0
2、WebEngine 加载url:
SCHEDULED
RUNNING
finish
程序运行不能结束。
如果在WebEngin类的dispatchLoadEvent(long frame, int state, String url, String contentType, double workDone, int errorCode)方法里面debug运行,则输出
SCHEDULED
RUNNING
finish
<html>
...//内容太多此处省略
</html>Process finished with exit code 0
二、分析原因
通过run和debug模式的运行的结果不同,让我意识到WebEngine 变量是局部变量,里面的监听事件还没有执行完就被gc回收。
备注:Stage primaryStage是局部变量,没有调用show方法,很快就被gc回收,里面包含的WebEngine、WebView和Scene也会被gc回收,故看不到输出。
以前还有遇见过这样的现象,比如Javafx的MediaPlayer播放几秒钟音乐就停止了,因为这个有播放声音比较明显,可以找到是gc回收了MediaPlayer。
import com.sun.javafx.application.PlatformImpl;
import javafx.concurrent.Worker.State;
import javafx.scene.web.WebEngine;public class Test {public static void main(String[] args) throws InterruptedException {PlatformImpl.startup(() -> {doDom();System.out.println("finish");});}static WebEngine webEngine;//webEngine必须要有引用在使用,不能定义成局部变量,否则被gc回收就没有输出结果public static void doDom(){//WebEngine webEngine = new WebEngine();//以前定义成局部变量,被gc回收了webEngine = new WebEngine();webEngine.getLoadWorker().stateProperty().addListener((obs, oldValue, newValue) -> {if (newValue == State.SUCCEEDED) {String html = (String) webEngine.executeScript("document.documentElement.outerHTML");System.out.println(html);System.exit(0);}System.out.println(newValue + " ");}); // addListener()webEngine.load("https://www.w3cschool.cn/java/javafx-webengine.html");//webEngine.loadContent(html);//直接加载html}
}