如何为 Java 应用程序创建安装程序
在 Java 中编写桌面应用程序时,我们总是希望其外观和感觉能够尽量贴近原生应用程序。因为一个优秀的应用程序应该要能融入其中,为用户提供已经熟悉的体验。
Swing GUI 的外观和感觉: Metal vs. Native
在桌面上,用户的使用旅程并不是从应用程序本身开始的,而是从安装程序开始的。这是 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_16
targetCompatibility = 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() {
@Override
public 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.DEB
linuxPackageName = "pomodoro"
}
windows {
type = org.panteleyev.jpackage.ImageType.MSI
winDirChooser = true
winMenu = 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