添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
很久以前听说过一种写法 比如写个安卓程序,刚学几天还不太会写安卓,就把每个页面都写成本地的html,再用WebView嵌套。 图方便,运行效率低,用户体验差,曾几何时,这是一种很糟嫌弃的写法 没想到,等我学JavaFX,学到吐血的时候,发现JavaFX也可以WebView嵌套Html 比起百度半天答非所问 全英文文档啃到吐血 html我不到5分钟就能写一个页面 一不小心真香了属于是 `嘲笑WebView` => `质疑WebView` => `理解WebView` => `成为WebView` ![滑稽](/api/file/getImage?fileId=64eebb72da74050014007df8)

## 折腾 最终目的是希望写个Demo 前端展示全部用本地的Html文件 后端逻辑控制全部用Java和JS交互实现 以达到一个节省学习JavaFX前端组件的目的 环境大致是这样 系统 `LinuxDeepin 20.9` IDE `IntelliJ IDEA 2023.1.2 (Ultimate Edition)` 环境 `OpenJDK 11.0.9` **新建JavaFX项目** 上一篇文章详细讲过了,这里略过 地址: [2023 IDEA 开发桌面图形界面程序 JavaFX 并导出 Jar 支持多系统](https://zzzmh.cn/post/eee76yv5u9e48dkarmzymnfryohapwm0) **稍作修改** 由于不用写JavaFX的组件模块,直接简化开发,删除了fxml和相关依赖代码,全部用Java代码即可搞定 maven核心依赖是这三个 `pom.xml` ```xml org.openjfx javafx-controls 17.0.6 org.openjfx javafx-web 17.0.6 手动引入 `javafx.web` 到模块化 `module-info.java` ```java module com.zzzmh.jfx { requires javafx.controls; // 手动引入到模块化 requires javafx.web; exports com.zzzmh.jfx; 删除了不需要的文件 最终目录如下 ```treeview JFX-WebView-Demo/ |-- src/ | |-- main/ | | |-- java/ | | | |-- com.zzzmh.jfx/ | | | | |-- controller/ | | | | | |-- Controller.java | | | | |-- App.java | | | |-- module-info.java | | |-- resources/ | | | |-- com.zzzmh.jfk/ | | | | |-- index/ | | | | | |-- index.html `-- pom.xml `index.html` 先随便写点东西占位 ```html JavaFX WebView Demo

测试

最后改下启动类 把fxml去掉,改为WebView作为最外层Panel WebView直接加载本地的index.html显示 ```java package com.zzzmh.jfx; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.web.WebView; import javafx.stage.Stage; public class App extends Application { @Override public void start(Stage stage) { WebView webView = new WebView(); webView.getEngine().load(getClass().getResource( "/com/zzzmh/jfx/html/index.html").toExternalForm()); Scene scene = new Scene(webView, 600, 400); stage.setTitle("JavaFX WebView Demo"); stage.setScene(scene); stage.show(); public static void main(String[] args) { launch(); 执行`App.java` `main`方法 一次跑通! ![截图](/api/file/getImage?fileId=64eee1e5da74050014007e23) 既然能用html作为前端显示,那写个页面就是分分钟的事情了 目前只剩下最后一个问题 不像node js可以直接调用系统api 这里的js只是webview内部的脚本 连接系统的是靠java代码 如果用传统方法,java写个api接口,js调接口,相当于是机关枪打蚊子 所以下一步的思路就是js直接调用java方法,实现交互逻辑 **JS调用Java方法 前后端交互** 参考了这几个链接 [JavaFX学习之在javascript中调用javaFX中提供的java方法](https://blog.csdn.net/zy103118/article/details/127425406) [JavaFx Webview 与js(vue)交互](https://blog.csdn.net/weixin_44517645/article/details/128180261) 大致代码如下 `Controller.java` ```java package com.zzzmh.jfx.controller; public class Controller { * 获取后端数据 public String getData() { // 这里假装去数据库查询了一套json数据 return "{\"name\":\"张三\",\"age\":9}"; `App.java` ```java package com.zzzmh.jfx; import com.zzzmh.jfx.controller.Controller; import javafx.application.Application; import javafx.beans.value.ObservableValue; import javafx.concurrent.Worker; import javafx.scene.Scene; import javafx.scene.web.WebEngine; import javafx.scene.web.WebView; import javafx.stage.Stage; import netscape.javascript.JSObject; public class App extends Application { @Override public void start(Stage stage) { WebView webView = new WebView(); WebEngine engine = webView.getEngine(); // 注入方法 engine.getLoadWorker().stateProperty().addListener( (ObservableValue ov, Worker.State oldState, Worker.State newState) -> { if (newState == Worker.State.SUCCEEDED) { // 获取JS的window对象 JSObject window = (JSObject) engine.executeScript("window"); // 讲controller注入到window对象中 window.setMember("controller", new Controller()); // 加载页面 engine.load(Controller.class.getResource( "/com/zzzmh/jfx/html/index.html").toExternalForm()); Scene scene = new Scene(webView, 600, 400); stage.setTitle("JavaFX WebView Demo"); stage.setScene(scene); stage.show(); public static void main(String[] args) { launch(); `index.html` ```html JavaFX WebView Demo

功能测试

然后就遇到问题了 无论怎么点`获取数据`按钮都没有反应 后端的 `public String getData()` 方法里打断点,也没任何反应 关键看不到报错信息,Java的控制台没报错,前端JS的控制台看不到 百度半天也没查到有用信息 于是没办法我只能用一个笨办法来查报错了 前端html里加个try,从div输出报错看看 `index.html` ```html
错误信息终于打出来了 `java.lang.IllegalAccessException: module javafx.web cannot access class com.zzzmh.jfx.controller.Controller (in module com.zzzmh.jfx) because module com.zzzmh.jfx does not open com.zzzmh.jfx.controller to javafx.web` 好家伙!这问题不用百度我就知道了,模块化里没写opens,只能说怪我 `java 11` 没学好,知识水平还永久性的停留在了`java 8` 补上模块化配置 `module-info.java` ```java module com.zzzmh.jfx { requires javafx.controls; requires javafx.web; requires jdk.jsobject; // 关键是这行代码 opens com.zzzmh.jfx.controller to javafx.web; exports com.zzzmh.jfx; 解决完这个问题,终于是跑通了,js可以直接获得java的数据,这样java连数据库就可以获取数据库里的数据了 ![截图](/api/file/getImage?fileId=64eef82cda74050014007e4e) 到这里基本已经大功告成了 顺手再写几个可能以后用得到的简单方法 `Controller.java` ```java package com.zzzmh.jfx.controller; import javafx.application.Platform; import javafx.scene.Scene; import javafx.scene.web.WebEngine; import javafx.scene.web.WebView; import javafx.stage.Stage; public class Controller { * 获取后端数据 public String getData() { // 这里假装去数据库查询了一套json数据 return "{\"name\":\"张三\",\"age\":9}"; * 新开一个窗口 public void open() { WebView webView = new WebView(); WebEngine engine = webView.getEngine(); engine.load(Controller.class.getResource( "/com/zzzmh/jfx/html/new.html").toExternalForm()); Scene scene = new Scene(webView, 400, 280); Stage stage = new Stage(); stage.setTitle("新开页面"); stage.setScene(scene); stage.show(); * 彻底退出程序 public void exit(){ Platform.exit(); (配合上文新开窗口这个功能,需在`resources/com.zzzmh.jfx/html/` 下新开一个 `new.html`,内容随意写点,略过) `index.html` ```html JavaFX WebView Demo

功能测试

最终执行效果 没有任何问题 ![截图](/api/file/getImage?fileId=64eef92dda74050014007e50) **打包二进制** 到这里就大功告成了 结束之前再复习一下上节课研究的打包二进制 复习上节课地址: [2023 IDEA 开发桌面图形界面程序 JavaFX 并导出 Jar 支持多系统](https://zzzmh.cn/post/eee76yv5u9e48dkarmzymnfryohapwm0) 先确保一下maven配置正确 完整maven配置如下 关键是mainClass要对其启动类App.java的main方法 `pom.xml` ```xml 4.0.0 com.zzzmh JFX-WebView-Demo 1.0-SNAPSHOT JFX-WebView-Demo UTF-8 5.9.2 org.openjfx javafx-controls 17.0.6 org.openjfx javafx-web 17.0.6 org.apache.maven.plugins maven-compiler-plugin 3.11.0 11 11 org.openjfx javafx-maven-plugin 0.0.8 default-cli com.zzzmh.jfx/com.zzzmh.jfx.App app app app true true true 随后在IDEA的maven中执行 `javafx/javafx:run` 看看能否启动成功 如果成功就可以在maven中执行 `javafx/javafx:jlink` 打包 ![截图](/api/file/getImage?fileId=64eefbc7da74050014007e53) 最终会在根目录 `target` 文件夹下得到打包后的文件 其中 `app.zip` 是所有文件的压缩包 只有 `66.1MB` 这是在包含了所有运行环境 即 `OpenJDK 11` 以及 `javafx.web` 浏览器内核 的情况下,以及算非常小了 通过shell命令就可以执行二进制文件,来启动这个桌面程序 ```shell cd target/app/bin ./app 结果在这里遇到一堆问题 1. 之前一直存在的渲染问题,css在初始化页面的时候不生效 2. 直接执行App的main方法和javafx:run都正常,但jlink打包后的程序用shell执行,前端就找不到window下的controller类 最后做了这几个修改解决 在WebView外面套了一层BorderPane,解决了WebView的渲染问题 在`engine.getLoadWorker().stateProperty().addListener`的外面申明Controller类,在里面传入参数(之前是直接在里面new一个类有问题) 修改后最终解决了上述2个问题 完整代码如下 `App.java` ```java public class App extends Application { @Override public void start(Stage stage) { BorderPane pane = new BorderPane(); WebView webView = new WebView(); WebEngine engine = webView.getEngine(); Controller controller = new Controller(); // 注入方法 engine.getLoadWorker().stateProperty().addListener( (ObservableValue ov, Worker.State oldState, Worker.State newState) -> { if (newState == Worker.State.SUCCEEDED) { // 获取JS的window对象 JSObject window = (JSObject) engine.executeScript("window"); // 讲controller注入到window对象中 window.setMember("controller", controller); // 加载页面 engine.load(Controller.class.getResource( "/com/zzzmh/jfx/html/index.html").toExternalForm()); pane.setCenter(webView); Scene scene = new Scene(pane, 600, 400); stage.setTitle("JavaFX WebView Demo"); stage.setScene(scene); stage.show(); public static void main(String[] args) { launch(); 重新执行了javafx:jlink 用shell cd到bin目录 执行二进制app 成功,功能全部正常 ```shell cd target/app/bin ./app ![](https://leanote.zzzmh.cn/api/file/getImage?fileId=64c9c860da74050014005b69) ## END 本文所有源码已发布到开源平台 源码地址: [https://gitee.com/zzzmhcn/JFX-WebView-Demo](https://gitee.com/zzzmhcn/JFX-WebView-Demo) [https://github.com/zzzmhcn/JFX-WebView-Demo](https://github.com/zzzmhcn/JFX-WebView-Demo) 最后补充一下jlink我目前尚未解决的一些问题 jlink简单来说就是按需引入的打包,把所有需要的环境都打包成二进制文件 可以直接在windows mac linux执行 (目前测试需要在对应系统打包,比如win打包的只能win执行,linux打包只能linux执行) 最大的优点就是小和方便,正常一个程序连依赖20MB,JDK需要200~300MB 而且JDK需要配置环境变量,且占用系统默认的JDK的位置 jlink打包后才30~60mb,就可以实现不入侵系统直接运行二进制文件 目前一个已知的缺点就是模块化,需要所有代码都是模块化开发,才可以jlink打包 目前已知的例如jedis jdbc都不支持模块化,一旦用了这些依赖就无法打包 我百度了说也有解决办法,但我暂时还没时间深入折腾,以后如果研究出来了再更新