在 Java 中编写桌面应用程序时,我们总是希望其外观和感觉能够尽量贴近原生应用程序。因为一个优秀的应用程序应该要能融入其中,为用户提供已经熟悉的体验。
在桌面上,用户的使用旅程并不是从应用程序本身开始的,而是从安装程序开始的。这是 Java 世界过去落后的地方。但现在已不再如此。
从 Java 16 开始,JDK 附带了 jpackage
工具。该工具可以将应用程序打包成带有内置 JRE 的捆绑包,并将其包装成原生安装程序和可执行文件。
在本篇文章中,我将演示如何将 jpackage 与 JxBrowser 应用程序结合使用。我将创建一个简单的 JxBrowser 应用程序,然后使用 jpackage 将其打包为原生安装程序和可执行程序。我会提供一些代码片段,您可以将其直接复制到您的项目中。
配置 Gradle
我的目标是创建一个简单的 Pomodoro tracker(番茄工作法跟踪器),安装它,并将其作为原生操作系统应用程序启动。它将使用 Gradle 进行构建配置,使用 Swing 进行用户界面设计。
让我们从头开始创建一个空的 Gradle 项目:
$ gradle init --dsl kotlin --type basic --project-name jxbrowser-installer
打开 build.gradle.kts 文件并应用 Java 插件:
plugins {java
}group = "com.teamdev.examples"
version = "1.0"java {sourceCompatibility = JavaVersion.VERSION_16targetCompatibility = JavaVersion.VERSION_16
}repositories {mavenCentral()
}
我们的应用程序需要两个 JxBrowser 依赖项。其中一个包含带有 Chromium 二进制文件的核心 API,另一个实现 Swing 工具包支持。
让我们使用 JxBrowser Gradle 插件[1]来添加必要的依赖项。
plugins {…id("com.teamdev.jxbrowser") version "{gradle_plugin_version}"
}jxbrowser {version = "{version}"
}dependencies {implementation(jxbrowser.swing)implementation(jxbrowser.currentPlatform)
}
在上面的代码片段中,我使用了 jxbrowser.currentPlatform
来检测当前平台并仅挑选必要的 Chromium 二进制文件。如果您正在构建一个仅适用于 Windows 的应用程序,您可以明确指定 Windows 平台的 Chromium 二进制文件:
dependencies {implementation(jxbrowser.swing)implementation(jxbrowser.win64)
}
应用程序代码
我们的应用程序非常简单。它会启动一个 JFrame
窗口,然后在该窗口中添加 BrowserView
组件,用于加载和显示包含 Pomodoro Tracker 的网页。
package com.teamdev.examples;import com.teamdev.jxbrowser.engine.Engine;
import com.teamdev.jxbrowser.view.swing.BrowserView;import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;import static com.teamdev.jxbrowser.engine.RenderingMode.HARDWARE_ACCELERATED;
import static javax.swing.SwingConstants.CENTER;
import static javax.swing.WindowConstants.DISPOSE_ON_CLOSE;/*** 一个 Pomodoro tracker.** 这款应用程序会显示一个带有集成浏览器组件的窗口,* 该组件负责加载并显示 Pomodoro Tracker 的网页应用。*/
public final class PomodoroTracker {public static final String URL = "https://teamdev.com/jxbrowser/docs/tutorials/jpackage/pomodoro.html";public static void main(String[] args) {var splash = showSplashScreen();showBrowser();splash.dispose();}private static void showBrowser() {var engine = Engine.newInstance(HARDWARE_ACCELERATED);var browser = engine.newBrowser();var frame = new JFrame("Pomodoro Tracker");frame.addWindowListener(new WindowAdapter() {@Overridepublic void windowClosing(WindowEvent e) {engine.close();}});var view = BrowserView.newInstance(browser);frame.add(view, BorderLayout.CENTER);frame.setDefaultCloseOperation(DISPOSE_ON_CLOSE);frame.setSize(1280, 900);frame.setLocationRelativeTo(null);frame.setVisible(true);browser.navigation().loadUrl(URL);}private static JWindow showSplashScreen() {var splash = new JWindow();splash.getContentPane().add(new JLabel("加载中...", CENTER));splash.setBounds(500, 150, 300, 200);splash.setVisible(true);return splash;}
}
打包应用程序
用 Java 编写的桌面应用程序需要在运行时携带它们所需的所有必要库。通常的做法是将库与应用程序合并为一个大的 Uber JAR。这需要一些额外的配置,并且不适合模块化项目。
现在有一个更简单的选择:将一堆 JAR 文件收集到一个文件夹中,然后让 jpackage 处理其余部分。
首先,让我们来配置构建主应用程序的 JAR 文件:
val jarDirectory = file("$buildDir/jars")
tasks {jar {manifest {attributes["Main-Class"] = "com.teamdev.examples.PomodoroTracker"}archiveFileName.set("main.jar")destinationDirectory.set(jarDirectory)}
}
接着,让我们创建一个任务来收集依赖项的 JAR 文件:
val jarDirectory = file("$buildDir/jars")
tasks {…register<Copy>("gatherDependencies") {from(configurations.runtimeClasspath).into(jarDirectory)}
}
这两个任务足以从命令行启动应用程序:
$ ./gradlew jar gatherDependencies
$ java -cp "build/jars/*" com.teamdev.examples.PomodoroTracker
现在一切准备就绪,可以配置 jpackage 了。
jpackage 是一个命令行工具。但是,我更倾向于将所有内容都保留在 Gradle 脚本中。与一堆 .sh 和 .bat 文件相比,Gradle 脚本更容易阅读和维护。
我推荐使用 org.panteleyev.jpackage
插件。 此插件会将 jpackage 的命令行 API 封装到 Gradle DSL 中。应用方法如下:
plugins {...id("org.panteleyev.jpackageplugin") version "1.3.1"
}
以下是我如何配置它来生成安装程序的方法:
val jarDirectory = file("$buildDir/jars")
tasks {…jpackage {// 在打包之前收集所有 JAR 文件。dependsOn("jar", "gatherDependencies")appName = "Pomodoro Tracker"appVersion = "${project.version}"// 包含 JAR 文件的目录。input = jarDirectory.absolutePath// 可启动的主要 JAR 文件的名称。mainJar = "main.jar"// 需要包含到捆绑 JRE 中的必要模块列表。// "java.logging" 是 JxBrowser 所必需的。addModules = listOf("java.base", "java.desktop", "java.logging")// JRE 模块的路径。modulePaths = listOf("${System.getProperty("java.home")}/jmods")// 安装程序存放的目录。destination = "$buildDir/dist"linux {type = org.panteleyev.jpackage.ImageType.DEBlinuxPackageName = "pomodoro"}windows {type = org.panteleyev.jpackage.ImageType.MSIwinDirChooser = truewinMenu = true}mac {type = org.panteleyev.jpackage.ImageType.DMG}}
}
完成所有配置后,接下来要做的就是调用 jpackage 任务。任务完成后,我们会在 build/dist 目录下找到安装程序:
$ ./gradlew jpackage
对于 Windows,您将需要 https://wixtoolset.org/releases/
安装程序实操视频
Create installer for Java App
源代码
您可以在 GitHub 存储库[2]中找到该应用程序的源代码。
相关链接
- JxBrowser 官方页面[3]。
- GitHub上的这个示例[4]。
- 其他 JxBrowser 示例[5]。
参考资料:
[1] JxBrowser Gradle 插件: https://plugins.gradle.org/plugin/com.teamdev.jxbrowser?utm_source=csdn&utm_medium=article&utm_campaign=installer-for-java-app
[2] GitHub 存储库: https://github.com/vlad-lubenskyi/jxbrowser-installer/?utm_source=csdn&utm_medium=article&utm_campaign=installer-for-java-app
[3] JxBrowser 官方页面: https://teamdev.cn/jxbrowser/?utm_campaign=installer-for-java-app&utm_medium=article&utm_source=csdn
[4] GitHub上的这个示例: https://github.com/vlad-lubenskyi/jxbrowser-installer/?utm_source=csdn&utm_medium=article&utm_campaign=installer-for-java-app
[5] 其他 JxBrowser 示例: https://github.com/TeamDev-IP/JxBrowser-Examples?utm_source=csdn&utm_medium=article&utm_campaign=installer-for-java-app