多年来,SWT 一直内置一个 Browser 组件。这是一个依赖于操作系统自带的 Web engine 的简单组件。该组件可以很好地显示网页并处理简单的任务,但对于需要跨平台行为一致、更好地控制 Engine、隔离用户数据等更高级需求来说,它显然不够用。
因此,许多开发人员开始寻求像 JxBrowser 这样的替代方案。JxBrowser 拥有更丰富的 API 和跨平台统一的 Browser engine,是内置 Browser 最受欢迎的替代方案。
在本指南中,我们提供了替换 SWT Browser
API 中每个方法的代码示例,以及相关 JxBrowser 文档的链接。
本指南主要介绍如何迁移到 JxBrowser。要了解为什么需要迁移,请查看选择 JxBrowser 还是 SWT Browser 博客文章,其中我们解释了这两种解决方案在技术和架构上的差异。
JxBrowser
JxBrowser 是一个商业库,可以将 Chromium 嵌入到 Java 应用程序中。它相比内置的 Browser
组件提供了更多功能,并提供原生支持 SWT 和 Eclipse RCP 应用程序的 Web 视图组件。
在所有操作系统上,JxBrowser 均使用其自身内置的同一版本 Chromium 内核。这确保了在不同平台上的行为和渲染效果始终稳定一致。
作为一款商业软件,JxBrowser 提供不限时的技术支持、Bug 修复服务,以及功能定制请求支持。
内置浏览器与 JxBrowser 的 API 接口范围。
SWT 项目的依赖项
将 JxBrowser 添加到项目非常简单,只需将几个 JAR 文件添加到类路径(classpath)即可。例如,在 Windows 上运行的 SWT 应用程序需要以下文件:
jxbrowser-8.7.0.jar
。此文件包含大部分 JxBrowser API。jxbrowser-swt-8.7.0.jar
。此文件包含 JxBrowser 的 SWT 组件。jxbrowser-win64-8.7.0.jar
。此文件包含适用于 64 位 Windows 的 Chromium 二进制文件。
您可以从 JxBrowser 8.7.0 版本发布说明下载所需的文件。
如果您使用的是普通的 Maven 或 Gradle,您可以按照常规方式添加 JxBrowser,即从我们的 Maven 仓库添加依赖项。
Eclipse RCP 的依赖项
如果您正在使用 Apache Tycho,那大概已经深有体会——一切都没那么简单。不过别担心,我们已经为您准备了详细的教程。在将 JxBrowser 集成到 Eclipse RCP 应用程序的教程中,您将了解:
- 如何使用 Maven 在构建时下载 JxBrowser 依赖项。
- 如何使用 Fragments(片段) 为多个平台分发 Chromium 二进制文件。
迁移
线程安全
与 SWT 中其他 UI 组件一样,Browser
组件只能在 UI 线程中使用。
JxBrowser 是线程安全的:您可以在不同的线程中安全地使用 JxBrowser 对象。但请注意,在 UI 线程中调用 JxBrowser API 时,它的许多方法是同步的。为了避免用户体验问题,我们通常不建议在 UI 线程中调用 JxBrowser。
创建 Browser
在 SWT 中,创建 Browser 非常简单。以下代码片段将 Web 视图组件添加到 parent
并加载网页:
var browser = new Browser(shell, SWT.NONE);
browser.setUrl("https://www.example.com");
...
// 当不再需要 Browser 时,将其释放。
browser.dispose();
在 JxBrowser 中,类似的逻辑如下:
var engine = Engine.newInstance(HARDWARE_ACCELERATED);
var browser = engine.newBrowser();
var view = BrowserView.newInstance(shell, browser);
browser.navigation().loadUrl("https://example.com");
...
// 当不再需要 Browser 时,将其关闭。
browser.close();
// 或者关闭 Engine。稍后会详细介绍两者的区别。
engine.close();
如您所见,JxBrowser 的代码更多,但控制力也更强。
现代 Browser 拥有复杂的进程模型、安全特性以及多种渲染 Web 内容的方式。然而,SWT 的 Browser
API 并未体现出这种复杂性,因为它最初的设计理念就是简单且不依赖特定 Engine。
而 JxBrowser 则完全基于 Chromium engine,并赋予您更加精细的控制能力。让我们拆解一下前面的代码片段,看看这种控制具体体现在哪些方面。
首先,我们创建一个 Engine
实例,它会启动 Chromium 主进程。您可以将其理解为在计算机上打开 Google Chrome:
var engine = Engine.newInstance(HARDWARE_ACCELERATED);
接下来,我们创建一个新的 Browser。您可以将其视为 Google Chrome 中的一个标签页。与标签页一样,您可以根据需要创建任意数量的 Browser
实例:
var browser = engine.newBrowser();
创建的 Browser 对象已完全可用。您目前无法看到它,但您可以加载网页并从中获取信息:
browser.navigation().loadUrl("https://example.com");
最后,您可以通过添加 SWT 组件 BrowserView
在 UI 中显示 Browser:
var view = BrowserView.newInstance(shell, browser);
我们在文档中详细描述了这一点以及其他架构细节。
关闭 Browser
在 SWT 中,当您不再需要 Browser 时,应该将其释放:
browser.dispose();
在 JxBrowser 中,您也需要关闭 Browser,但您可以选择:关闭单个“标签页”,或者一次性关闭整个 Engine 及所有已创建的 Browser:
// 您可以关闭单个 "标签页"。
browser.close()
// 或者,您可以关闭整个 Engine,这将自动关闭其中创建的所有 Browser。
engine.close();
在 SWT 中,您可以接收浏览器关闭的通知,但该通知仅在 JavaScript 通过调用 window.close()
关闭页面时才会触发:
browser.addCloseWindowListener(event -> {
// Browser 已被 JavaScript 关闭。
});
而在 JxBrowser 中,无论 Browser 以何种方式关闭,都会触发对应的等效事件:
browser.on(BrowserClosed.class, event -> {
// Browser 已被 JavaScript 关闭,
// 或通过调用 browser.close() 或 engine.close() 关闭。
});
导航方法
SWT Browser 中的大多数导航方法在 JxBrowser 中都有一一对应的实现:
browser.setUrl("https://example.com");
var url = browser.getUrl();
browser.reload();
browser.stop();
browser.back();
browser.forward();
var canGoBack = browser.isBackEnabled();
var canGoForward = browser.isForwardEnabled();
var navigation = browser.navigation();
navigation.loadUrl("https://example.com");
var url = browser.url();
navigation.reload();
navigation.stop();
navigation.goBack();
navigation.goForward();
var canGoBack = navigation.canGoBack();
var canGoForward = navigation.canGoForward();
您可以在两种 Browser 中加载包含 POST 负载和附加标头的 URL。在 SWT 中,您可以将数据和标头指定为原始字符串。而在 JxBrowser 中,我们提供了更高级别的 API:
var url = "https://example.com/login";
var postData = "username=user&password=secret";
var headers = new String[] {
"Content-Type: application/x-www-form-urlencoded",
"Authorization: Bearer xyz1234"
};
browser.setUrl(url, postData, headers);
var url = "https://example.com/login";
var formData = FormData.newBuilder()
.addPair(Pair.of("username", "user"))
.addPair(Pair.of("password", "secret"))
.build();
var header = HttpHeader.of("Authorization", "Bearer xyz1234");
var params = LoadUrlParams.newBuilder(url)
.addExtraHeader(header)
.uploadData(formData)
.build();
navigation.loadUrl(params);
位置监听器
在 SWT 中,您可以注册一个 LocationListener
来监听 Browser 中加载的位置何时发生变化。在 JxBrowser 中,您可以通过订阅 NavigationFinished
事件来实现这一点:
browser.addLocationListener(new LocationAdapter() {
@Override
public void changed(LocationEvent event) {
var url = event.location;
var isMainFrame = event.top;
}
});
navigation.on(NavigationFinished.class, event -> {
var url = event.url();
var frame = event.frame();
var hasCommitted = event.hasCommitted();
var isSameDocument = event.isSameDocument();
var isErrorPage = event.isErrorPage();
if (isErrorPage) {
var error = event.error();
}
});
JxBrowser 中共有 9 种细粒度的导航事件。请查看完整列表。
在 SWT 中,LocationListener
还可用于阻止不必要的导航。在 JxBrowser 中,您可以通过注册 StartNavigationCallback
来实现这一点:
browser.addLocationListener(new LocationAdapter() {
@Override
public void changing(LocationEvent event) {
var url = event.location;
if (...) {
event.doit = true;
} else {
event.doit = false;
}
}
});
navigation.set(StartNavigationCallback.class, params -> {
var url = params.url();
var isRedirect = params.isRedirect();
var isMainFrame = params.isMainFrame();
if (...) {
return StartNavigationCallback.Response.start();
} else {
return StartNavigationCallback.Response.ignore();
}
});
进度监听器
在 SWT 中,您可以接收到页面加载进度的事件,尤其是在页面完全加载完成时的通知。
而在 JxBrowser 中,虽然无法查看加载进度,但可以检测页面是否已完全加载,并据此执行操作:
browser.addProgressListener(new ProgressAdapter() {
@Override
public void completed(ProgressEvent event) {
// 某个位置已完全加载。
}
});
// 检查 HTML 文档是否已完全加载。
browser.navigation().on(FrameDocumentLoadFinished.class, event -> {
var frame = event.frame();
var isMainFrame = frame.isMain();
frame.document().ifPresent(document -> {
var element = document.documentElement();
});
});
JxBrowser 不提供与 ProgressLister.changed(ProgressEvent)
方法等效的 API。
从 Java 调用 JavaScript
在 SWT 中,您可以通过调用 execute()
和 evaluate()
方法在当前文档中执行 JavaScript 代码。如果可能,evaluate()
方法会自动将返回值转换为 Java 原始类型和 String
。这两种方法都是同步执行的:
boolean isSuccessful = browser.execute("document.documentElement");
String zipCode = browser.evaluate("getZipCode()");
在 JxBrowser 中,您可以在任意 Frame 上下文中执行 JavaScript,并且 JxBrowser 会将返回值自动转换为 11 种可用类型 之一:
Element element = frame.executeJavaScript("document.documentElement");
String zipCode = frame.executeJavaScript("getZipCode()");
// 也有异步版本:
frame.executeJavaScript("longOperation()", (result) -> {
// 非阻塞 JavaScript 调用的结果。
});
在 SWT 中,您可以启用或禁用 JavaScript。在 JxBrowser 中,我们提供了这些方法的直接替代方案:
browser.setJavascriptEnabled(false);
var isEnabled = browsser.getJavascriptEnabled();
var settings = browser.settings();
settings.enableJavaScript();
settings.disableJavaScript();
var isEnabled = settings.isJavaScriptEnabled();
从 JavaScript 调用 Java
在 SWT 中,若要从 JavaScript 调用 Java 代码,需要注册 BrowserFunction
。该函数在被释放前,可在所有网页中访问。其参数类型和返回值将自动转换为相应的 Java 原始类型和 String
。
// 该函数将在顶部 Frame 和其他两个 Frame 中以 `sayHello()` 的形式提供。
var function = new BrowserFunction(br, "sayHello") {
@Override
public Object function(Object[] args) {
return "Hello from Java!";
}
};
...
function.dispose();
在 JxBrowser 中,您可以将任意 Java 对象注入 JavaScript,但这一操作只能在 Frame 就绪时进行。由于在页面重新加载时,注入的对象会自动关闭,因此您无需手动处理。
// 此回调在文档创建后、页面执行自身脚本之前调用。
browser.set(InjectJsCallback.class, params -> {
var frame = params.frame();
JsObject window = frame.executeJavaScript("window");
window.putProperty("sayHello", (JsFunctionCallback) args -> {
return "Hello from Java!";
});
return InjectJsCallback.Response.proceed();
});
加载 HTML
在这两种 Browser 中,您都可以从内存中加载简单的 HTML:
browser.setText("<html>Hello</html>");
frame.loadHtml("<html>Hello</html>");
在 JxBrowser 中,loadHtml()
方法是通过从给定的 HTML 加载数据 URL 来实现的,这使得页面大小被限制在约 2 MB。
若要突破这一限制或处理其他高级情况,请查阅我们的加载 HTML 与本地资源指南。
获取页面内容
在 SWT 中,您可以使用 Browser 的 getText()
方法来读取页面的 HTML 内容。在 JxBrowser 中,您需要获取文档的内部或外部 HTML:
var html = browser.getText();
frame.document().flatMap(Document::documentElement).ifPresent(element -> {
var innerHtml = element.innerHtml();
var outerHtml = element.outerHtml();
});
状态栏与页面标题
在两种 Browser 中,您都可以在状态栏发生变化时收到通知:
browser.addStatusTextListener(event -> {
var newStatus = event.text;
});
browser.on(StatusChanged.class, event -> {
var newStatus = event.statusText();
});
您可以用类似的方法跟踪页面标题的变化:
browser.addTitleListener(event -> {
var newTitle = event.title;
});
browser.on(TitleChanged.class, event -> {
var newTitle = event.title();
});
Cookies
在 SWT 中,您可以使用 getCookie()
方法读取现有的 Cookies。在 JxBrowser 中,您可以使用 CookieStore
读取 Cookies:
String cookie = Browser.getCookie("auth_token", "https://example.com");
var cookieStore = browser.profile().cookieStore();
for (Cookie cookie : cookieStore.cookies("https://example.com")) {
String domain = cookie.domain();
Timestamp expiration = cookie.expirationTime();
...
}
要创建新的 Cookie,可在 SWT 中使用 setCookie()
,在 JxBrowser 中使用 CookieStore.set()
。
Browser.setCookie("auth_token=abc123; Path=/;", "https://example.com");
var cookieStore = browser.profile().cookieStore();
var cookie = Cookie.newBuilder("example.com")
.name("auth_token")
.value("abc123")
.path("/")
.build();
cookieStore.set(cookie);
同样,您也可以清除 Cookies:
Browser.clearSessions();
// 删除特定的 Cookie。
cookieStore.delete(cookie);
// 删除所有 Cookie。
cookieStore.deleteAll();
身份验证
在 SWT 中,您需要注册一个 AuthenticationListener
来自动处理服务器的身份验证请求。在 JxBrowser 中,您需要使用 AuthenticateCallback
:
browser.addAuthenticationListener(event -> {
var url = event.location;
if (...) {
event.user = "username";
event.password = "password";
event.doit = true;
} else {
event.doit = false;
}
});
var network = browser.profile().network();
network.set(AuthenticateCallback.class, (params, action) -> {
var url = params.url();
var proxy = params.isProxy();
var scheme = params.scheme();
var hostPort = params.hostPort();
if (...) {
action.authenticate("username", "password");
} else {
action.cancel();
}
});
弹出窗口
当网页需要打开新的弹出窗口时,SWT 会向您发送一个事件。根据事件类型,操作系统会为您创建新的 Browser 窗口,或者让应用程序自行创建:
browser.addOpenWindowListener(event -> {
if (event.required) {
// 您的应用程序需要为弹出窗口创建一个窗口。
event.browser = new Browser(shell, SWT.NONE);
} else {
// 操作系统将创建弹出窗口。
}
});
JxBrowser 会自动为新的弹出窗口创建 Browser 对象。您只需在用户界面中显示它或在后台使用即可。默认情况下,JxBrowser 会在新的 Shell
中打开弹出窗口:
// 首先,允许创建弹窗 Browser。
browser.set(CreatePopupCallback.class, e -> {
var url = e.targetUrl();
var name = e.targetName();
if (...) {
return CreatePopupCallback.Response.create();
} else {
return CreatePopupCallback.Response.suppress();
}
});
browser.set(OpenPopupCallback.class, e -> {
var popupBrowser = e.popupBrowser();
var bounds = e.initialBounds();
var scaleFactor = e.scaleFactor();
// 您可以在此处将 `popupBrowser` 嵌入到 UI 中。
return OpenPopupCallback.Response.proceed();
});
JxBrowser 支持以编程方式处理 JavaScript 对话框、文件选择框、颜色选择器等各类对话框。具体用法请参考对话框指南。
结论
从 SWT Browser 迁移到 JxBrowser,本质上就是将 API 调用进行替换,因为大多数方法在 JxBrowser 中都有直接对应的实现。
在本指南中,我们为 SWT Browser 的每个方法提供了 JxBrowser 的直接替代方案,涵盖了整个 Browser API。
尽管在实际项目中迁移工作可能存在一定挑战,但我们相信,这一过程是可以清晰、有序推进的。希望本指南能够成为您使用 JxBrowser 替代 SWT Browser 的良好起点。
发送中。。。
您的个人 JxBrowser 试用密钥和快速入门指南将在几分钟内发送至您的电子邮箱。