如何为 Java 应用程序创建安装程序

在 Java 中编写桌面应用程序时,我们总是希望其外观和感觉能够尽量贴近原生应用程序。因为一个优秀的应用程序应该要能融入其中,为用户提供已经熟悉的体验。

 Swing GUI 的外观和感觉: Metal vs. Native

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_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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/147749.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

什么是频谱泄露?

参考&#xff1a;https://www.bilibili.com/video/BV17a411j7bH/?spm_id_from333.337.search-card.all.click&vd_source7a1a0bc74158c6993c7355c5490fc600 在理想情况下&#xff08;周期信号且时间无限&#xff09;&#xff0c;信号经过 FFT 后可以得到理想的频谱 但在现实…

【iOS】引用计数(一)

【iOS】引用计数 文章目录 【iOS】引用计数前言ARC与MRC什么是引用计数的机制内存管理的思考方式自己生成的对象非自己生成的对象不再需要自己持有就释放无法释放非自己持有的对象 autorelease小结 前言 笔者最近开始学习了一下有关于引用计数的内容&#xff0c;写这篇博客来简…

关于自动化测试的一点了解

一 自动化测试基础的认识 1)首先&#xff0c;什么是自动化测试&#xff1f; 自动化测试是把以人为驱动的测试行为转化为机器执行的一种过程。通常&#xff0c;在设计了测试用例并通过评审之后&#xff0c;由测试人员根据测试用例中描述的过程一步步执行测试&#xff0c;得到实…

史上最全!!!大厂面试真题-SpringBoot自动装配的原理是什么?

我想你也在真实面试中被问过无数次这个问题了&#xff0c;我也是&#xff0c;但是不管你怎么搜&#xff0c;都只有那几篇八股文的答案&#xff0c;你问GPT它都解释不清楚&#xff0c;我决定自己写一篇详细的&#xff0c;避免遗忘也想帮助一下患难中的兄弟姐妹们&#xff0c;能把…

struct的精确用法

目录 我终于回来啦&#xff01; 1,可以创造根据结构体格式的成员或数组。 普通成员 数组成员 2,可以用指针遍历成员 3,使用typedef --------------------------------------------------------------------------------------------------------------------------------…

代码随想录Day 52|题目:101.孤岛的面积、102.沉没孤岛、103.水流问题、104.建造最大岛屿

提示&#xff1a;DDU&#xff0c;供自己复习使用。欢迎大家前来讨论~ 文章目录 图论part03题目一&#xff1a;101.孤岛的总面积解题思路DFS**BFS** 题目二&#xff1a;102. 沉没孤岛解题思路 题目三&#xff1a;103. 水流问题解题思路优化 题目四&#xff1a;104.建造最大岛屿…

[Linux]用户管理指令

开机/重启/登录/注销 进入xhsell 或者虚拟系统中, 右键桌面打开终端, 在终端执行命令, 重启或关机linux系统 建议使用普通账号登录, 如果权限不够时, 使用 su - 用户名 命令切换到超管, 然后再使用 logout命令退回到普通账号, logout 不能在图形界面的终端中使用 用户管理 Li…

Centos7.9 使用 Kubeadm 自动化部署 K8S 集群(一个脚本)

文章目录 一、环境准备1、硬件准备&#xff08;虚拟主机&#xff09;2、操作系统版本3、硬件配置4、网络 二、注意点1、主机命名格式2、网络插件 flannel 镜像拉取2.1、主机生成公私钥2.2、为啥有 Github 还用 Gitee2.3、将主机公钥添加到 Gitee2.3.1、复制主机上的公钥2.3.2、…

最佳植树距离 - 华为OD统一考试(E卷)

2024华为OD机试&#xff08;C卷D卷E卷&#xff09;最新题库【超值优惠】Java/Python/C合集 题目描述 按照环保公司要求&#xff0c;小明需要在沙化严重的地区进行植树防沙工作&#xff0c;初步目标是种植一条直线的树带。由于有些区域目前不适合种植树木&#xff0c;所以只能在…

电脑提示找不到msvcp110.dll怎么办?全方面详细解答

msvcp110.dll 是 Microsoft Visual C 2012 Redistributable Package 中的一个动态链接库文件。它是运行使用 Visual C 2012 开发的应用程序所必需的&#xff0c;包含了许多 C 标准库函数的实现。这些函数主要用于支持字符串处理、内存管理、输入输出流、异常处理等功能。 1.ms…

Clion使用vcpkg管理C/C++包

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、Clion安装vcpkg二、使用步骤1.切换到清单模式2.开始安装包 三、测试代码总结 前言 Linux上的库基本都可以通过apt或yum等包管理工具来在线安装包&#xff…

C语言深入理解指针(四)

目录 字符指针变量数组指针变量数组指针变量是什么数组指针变量怎么初始化 二维数组传参的本质函数指针变量函数指针变量的创建函数指针变量的使用代码typedef关键字 函数指针数组转移表 字符指针变量 字符指针在之前我们有提到过&#xff0c;&#xff08;字符&#xff09;&am…

NLP 文本分类核心问题

解决思路 分解为多个独立二分类任务将多标签分类转化为多分类问题更换 loss 直接由模型进行多标签分类 数据稀疏问题 标注更多数据&#xff0c;核心解决方案&#xff1a; 自己构造训练样本 数据增强&#xff0c;如使用 chatGPT 来构造数据更换模型 减少数据需求增加规则弥补…

MELON的难题- 华为OD统一考试(E卷)

2024华为OD机试&#xff08;C卷D卷&#xff09;最新题库【超值优惠】Java/Python/C合集 题目描述 MELON 有一堆精美的雨花石&#xff08;数量为 n&#xff0c;重量各异&#xff09;&#xff0c;准备送给 S和 W&#xff0c;MELON 希望送给俩人的雨花石重量是一致的。请你设计一…

爬虫 ----hook

目录 定义&#xff1a; 了解什么是hook? 举例 hook XHR请求 XMLHttpRequest 案例地址&#xff1a; Interceptors-拦截器 HOOK cookie操作 cookie 示范 常见的hook代码总结 1.Hook Cookie 2.Hook Header 3.Hook URL 4.Hook JSON.stringify 5.Hook JSON.parse 6.Ho…

Mac使用gradle编译springboot-2.7.x源码

1 开发环境&#xff1a; JDK8 ideaIU-2024.2.2 gradle-7.6.3 代理网络 2 下载springboot源码 代码仓库网址 git clone -b 2.7.x https://github.com/spring-projects/spring-boot.git3 安装gradle gradle下载网址 https://services.gradle.org/distributions/ 安装此文件指…

C语言 | Leetcode C语言题解之第415题字符串相加

题目&#xff1a; 题解&#xff1a; char* addStrings(char* num1, char* num2) {int i strlen(num1) - 1, j strlen(num2) - 1, add 0;char* ans (char*)malloc(sizeof(char) * (fmax(i, j) 3));int len 0;while (i > 0 || j > 0 || add ! 0) {int x i > 0 ?…

lsof可以查看当前系统中正在被使用的文件,包括动态库

lsof的英文是 list open files lsof直接回车&#xff0c;会显示很多&#xff0c;可以配合more命令查看 lsof | more -10 sudo lsof | more -20 lsof查看正在使用某个动态库的进程 lsof /lib/x86_64-linux-gnu/libc.so.6 lsof /usr/lib/x86_64-linux-gnu/libc.so.6 l…

如何优化苹果CMS 泛目录的缓存管理?

在使用苹果CMS进行内容管理时&#xff0c;缓存管理是提升网站性能的重要环节。随着技术的不断发展&#xff0c;泛目录插件的缓存机制也逐渐变得不再必要。&#xff08;maccmscn&#xff09;本文将探讨如何在不使用缓存的情况下&#xff0c;优化苹果CMS泛目录的性能&#xff0c;…

(学习记录)使用 STM32CubeMX——配置时钟(入门)

使用STM32CubeMX配置STM32F103C8T6时钟部分 选择芯片 ①&#xff1a;选择MCU型号 ①&#xff1a;这里使用英文输入法&#xff0c;输入你想要的芯片型号&#xff0c;我这里采用STM32F103C8T6 ②&#xff1a;这里能看到搜索后出来的芯片具体型号&#xff0c;选择匹配度最高的一个…