常见问题
本指南将介绍如何解决一些常见问题。
Windows 上的启动失败
如果 JxBrowser 在 Windows 上无法启动,请确保环境满足系统要求并且没有杀毒软件干扰。
JxBrowser 在单独的本机进程中运行 Chromium。有时杀毒软件或本地安全策略不允许本机进程启动。即使 JxBrowser 可执行文件使用有效且受信任的签名进行签名,杀毒软件仍可能不允许运行不在其白名单中的程序。
如果上述操作没有帮助,请启用日志记录,重现问题,并向我们提供收集的日志消息。
Linux 上的启动失败
如果 JxBrowser 在 Linux 上无法启动,请确保环境满足系统要求并具有必要的系统库。
在一些 Linux 发行版中,由于缺少系统库,Chromium 进程可能无法启动。当发生这种情况时,库会将详细信息写入日志。
启用日志记录并检查日志消息是否包含如下错误:
java.lang.UnsatisfiedLinkError: /tmp/JxBrowser/VERSION/libbrowsercore_toolkit.so:
libgobject-2.0.so.0: cannot open shared object file: No such file or directory
要解决此问题,请安装缺少的库。
如果没有帮助,请启用日志记录,重现问题,并向我们提供收集的日志消息以及您的 Linux 发行版的名称和版本。
启动缓慢
启动时间取决于环境、硬件性能和安装的杀毒软件。
JxBrowser 启动过程包括几个步骤。首先,它提取存储在 JAR 文件中的 Chromium 二进制文件。然后启动主 Chromium 进程,并建立 Java 和 Chromium 进程之间的连接。
通常情况下,Chromium 二进制文件的提取只会在环境中发生一次。此步骤可能会明显减慢启动速度。例如,在 i7/16GB RAM/512GB SSD 机器上大概额外需要 3 秒。点击此处了解如何跳过此步骤。
杀毒软件可能会在允许 JxBrowser 启动库二进制文件之前检查它们。这会对启动时间产生负面影响。请尝试禁用杀毒软件,看看是否有所帮助。
如果没有帮助,请启用日志记录,重现问题,并向我们提供收集的日志消息和有关您硬件的详细信息。
Chromium 进程崩溃
JxBrowser 在单独的本机进程中运行 Chromium。此进程中的错误可能会导致进程意外终止。发生这种情况时,库会生成一个或多个崩溃转储文件。这些文件对于了解崩溃原因至关重要。
如果 Chromium 进程意外终止,并且您收到 EngineCrashed
事件,请报告该问题并附上生成的崩溃转储文件。
在 Java 应用程序中冻结
如果您的 Java 应用程序挂起,并且您认为它是由于 JxBrowser 而导致的,请按照以下步骤操作:
视频无法播放
JxBrowser 可能不支持您尝试播放的视频格式。请查看支持的视频和音频格式的列表。
如果其中一种支持的视频格式无法播放,请报告问题。
JMenu 重叠
在硬件加速渲染模式下,BrowserView
将覆盖菜单栏:
出现此问题的原因在于混合了轻量级的弹出菜单和重量级的 BrowserView
。要解决此问题,请禁用 Swing 弹出窗口的轻量级模式:
JPopupMenu.setDefaultLightWeightPopupEnabled(false);
JPopupMenu.setDefaultLightWeightPopupEnabled(false)
此代码强制所有 Swing 弹出菜单变为重量级。
在 Java 9+ 中使用 JxBrowser
在某些情况下,JxBrowser 需要访问非公共 API。在 Java 9+ 中,您必须显式授予对 JxBrowser 的访问权限。在本节中,我们列出了此类案例。
嵌入到 Swing 中
在 Windows 和 Linux 上的 HARWARE_ACCELERATED
渲染模式下,该库使用内部 JDK API 正确遍历 Java 和 Chromium 窗口之间的输入焦点。
如果您的应用程序是 non-modular 的,需要将所需的程序包导出到未命名的模块:
--add-exports java.desktop/sun.awt=ALL-UNNAMED
如果应用程序是 modular 的,则需要将所需的包导出到 JxBrowser 模块:
--add-exports java.desktop/sun.awt=jxbrowser.swing
嵌入到 JFXPanel
中
如果将 BrowserView 嵌入到 JFXPanel 内部的 JavaFX 中,该库将需要访问 JavaFX 的内部 API 以获取应用程序窗口的本机句柄。
如果您的应用程序是 non-modular 的,需要将所需的包导出到未命名的模块:
--add-opens javafx.swing/javafx.embed.swing=ALL-UNNAMED
--add-opens javafx.graphics/com.sun.javafx.stage=ALL-UNNAMED
--add-exports javafx.graphics/com.sun.javafx.stage=ALL-UNNAMED
--add-exports javafx.controls/com.sun.javafx.scene.control=ALL-UNNAMED
如果应用程序是 modular 的,则需要将所需的包导出到 JxBrowser 模块:
--add-opens javafx.swing/javafx.embed.swing=jxbrowser
--add-opens javafx.graphics/com.sun.javafx.stage=jxbrowser
--add-exports javafx.graphics/com.sun.javafx.stage=jxbrowser
--add-exports javafx.controls/com.sun.javafx.scene.control=jxbrowser
无法登录 Google 账户
尝试登录 Google 账户可能会出现以下消息:
出现此问题的原因可能是远程调试端口。配置端口后,Chromium 会阻止任何尝试登录其服务(例如 Gmail、YouTube)的行为。
Docker 内存不足
默认情况下,Docker 运行的容器 /dev/shm
共享内存空间为 64MB。这对于 Chromium 来说太少了,在渲染页面时会导致崩溃。
从 7.10 版本开始,JxBrowser 使用 /dev/shm
在离屏渲染模式下存储位图。它为每个网页分配额外的 ~10MB (1080p) 或 ~40MB (4k) 来存储位图。
要解决此问题,请使用 docker run --shm-size=1gb
命令运行 Docker 容器以增加 /dev/shm
的大小。
为新密钥环选择密码
在 Linux 上,Chromium 可能会创建一个新的密钥环,操作系统将显示此对话框:
为防止这种情况发生,请将 Chromium 配置为使用 BASIC
密码存储而不是系统密码存储:
var engine = Engine.newInstance(
EngineOptions.newBuilder(renderingMode)
.passwordStore(PasswordStore.BASIC)
.build());
val engine = Engine.newInstance(
EngineOptions.newBuilder(renderingMode)
.passwordStore(PasswordStore.BASIC)
.build())
ObjectClosedException
API 库中的服务对象可能已关闭。尝试调用已关闭的对象会导致 ObjectClosedException
异常。
在本指南中,我们将解释服务对象可能被关闭的常见场景以及您可以采取的措施。
已关闭的 Engine
JxBrowser 中最顶层的对象是 Engine。它可能在以下两种情况下被关闭:
- 调用了
Engine::close()
方法; - Main Chromium 进程发生错误。
如果您关闭 Engine,所有与之关联的对象(例如 Profiles、Browsers、Frames 等)都将自动关闭。因此,在关闭 Engine 后,无论出于何种原因,请确保不要使用与已关闭的 Engine 相关联的任何对象。
如果主 Chromium 进程发生错误,该进程将终止并关闭 Engine。当它发生时,库会生成崩溃转储文件并将它们存储在特定目录中。使用 EngineCrashed
事件在它发生时得到通知。
已删除的 Profile
可以通过调用 Profiles::delete()
方法删除 Profile。
如果您删除 Profile,所有与其关联的对象(例如 cookie 存储、浏览器、框架、DOM/JS 对象等)都会自动关闭。因此,在删除 Profile 后,请确保不要使用与已删除 Profile 相关联的任何对象。
已关闭的 Browser
有两种情况会导致 Browser
被关闭:
- 调用了
Browser::close()
方法; - JavaScript 关闭了窗口。
如果关闭 Browser,所有与之关联的对象(例如导航、文本查找器、Frames、DOM/JS 等)都将自动关闭。因此,在 Browser 关闭后,请确保不要使用与已关闭 Browser 相关联的任何对象。
JavaScript 中的 window.close()
方法只能关闭由另一个脚本打开的 Browser。如果您预料到会发生这种情况,请考虑在使用前检查 Browser 状态。
请注意,当 JavaScript 关闭 Browser 时,Java 会稍微延迟发现它。如果 Browser 已经关闭,但 Java 尚未被通知,那么尝试使用关闭的 Browser 将导致 ObjectClosedException
。
已卸载的网页
每个加载的网页都有一个 main frame 和 child frames。当导航到一个网页时,Browser 会卸载之前加载的页面并删除其中的所有 Frame 和 DOM/JS 对象。
确保在网页卸载后不要使用 DOM 和 JavaScript 对象。使用导航事件来获得通知。
请注意,Java 接收导航事件时有轻微延迟。如果页面已经卸载,但 Java 尚未被通知,那么尝试使用已删除/关闭的对象将导致 ObjectClosedException
。
已终止的渲染进程
在 Chromium 中,Frames 驻留在渲染进程中。当渲染过程终止时,Frames 将被删除。当 Frame 被删除时,该网页、其 DOM 和 JavaScript 对象也会被删除并关闭。
使用 RenderProcessTerminated
事件在进程终止时获得通知。如果进程意外终止,请报告问题并附上生成的崩溃转储文件。
如果它正常终止,则不需要任何操作。
Swing BrowserView
中的 InaccessibleObjectException
当 Swing BrowserView
获得焦点时,您可能会在控制台输出中看到以下异常:
Exception in thread "AWT-EventQueue-0" java.lang.reflect.InaccessibleObjectException: Unable to make static synchronized void java.awt.KeyboardFocusManager.setMostRecentFocusOwner(java.awt.Window,java.awt.Component) accessible: module java.desktop does not "opens java.awt" to unnamed module
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199)
at java.base/java.lang.reflect.Method.setAccessible(Method.java:193)
这是因为 JxBrowser 使用了 java.awt
包中的私有 API,而 Java 17 及更高版本不允许从一个模块访问另一个模块的内部。为避免此异常,您应在 JVM 参数中使用 --add-opens
开关及对应的包:
--add-opens=java.desktop/java.awt=ALL-UNNAMED