解决方案
使用warp-packer 从jlink 创建的图像和应用启动器中创建一个exe。
第一:
- 从download link 复制 warp-packer。
- 使用 jlink 为您的应用程序生成图像。
然后,创建单个文件应用程序可执行文件。这可以通过一个命令来完成(在一行上运行它并使用您的值而不是 %% 变量):
%WARP_DIR%\warp-packer
--arch windows-x64
--input_dir %APP_JLINK_IMAGE_DIR%
--exec %APP_JLINK_LAUNCHER_BAT_FILE%
--output %APP_SINGLE_EXECUTABLE_FILE_NAME%
该命令可以从命令行手动运行,也可以通过适当的构建工具插件自动运行。
可以使用最适合您的构建环境的方式调用 jlink;例如任何一种:maven 插件、gradle 插件、命令行实用程序、jpackage 实用程序或 jpackage 构建工具插件等。
相关答案
这个想法不是我的,是在这里提出的:
教程
如果需要更多信息,以下是完整的教程示例。
解决方案说明
这个答案很长,因为它试图提供一个完整的例子和额外的上下文建议。它可以更简洁。它的风格更接近于教程或博客文章风格的帖子,而不是 StackOverflow 的答案。希望长度不会令人生畏,并且很容易复制结果。
我对此很好奇,所以我想我会尝试一下。令我惊讶的是,我能够让它工作。所以我在这里记录了如何复制它。
关键是“OH GOD SPIDERS”在 cmets 中的建议,使用"warp" 工具进行打包,并结合其他建议与jlink 接口的 cmets。
我尝试尽可能多地使用Maven 构建工具,所以这个解决方案就是面向这个的。如果您愿意,可以将该解决方案调整到另一个工具链。
解决方案示例使用 FXML 构建 JavaFX 应用程序。如果不包含 FXML,该示例可能会更简单、更小,但我认为展示资源如何与此解决方案一起使用很重要,这就是包含 FXML 的原因。
限制
高级步骤
-
将应用程序构建为Java platform modular application。
- 应用程序必须没有自动依赖项(应用程序本身及其依赖的所有传递依赖项都必须定义为具有正确定义的 module-info.java 文件的 Java 模块)。
-
链接应用程序以使用启动脚本创建运行时映像。
-
将带有启动脚本的运行时映像转换为 exe。
程序
-
Install JDK 17(不需要包含 JavaFX 的版本)。
-
Install Maven。
- 创建如下所示的项目文件。
-
Install warp 到
tools\warp-packer.exe。
- 构建并打包项目 (
mvn package)。
- 运行应用程序 exe (
target/hellowarp.exe) 进行测试。
- 将应用程序 exe 提供给使用 Windows 机器的朋友。
- 朋友们将能够从命令行或通过双击该 exe 来运行该 exe。
无需安装您的应用程序,无需提取或解压缩任何档案,无需安装 Java 运行时,也无需其他额外安装。只需将 exe 文件复制到 Windows 机器上(例如单击网络上的下载链接或从邮件附件中复制 exe),然后双击该 exe 即可立即运行您的应用程序。
文件树
C:\dev\hellowarp>tree /a /f 。
卷本地磁盘的文件夹路径列表
卷序列号为 00000086 C034:A84E
C:\DEV\HELLOWARP
| pom.xml
|
+---src
| \ - -主要的
| +---java
| | |模块信息.java
| | |
| | \---com
| | \ - -例子
| | \---hellowarp
| | HelloController.java
| | HelloWarp.java
| |
| \ - -资源
| \---com
| \ - -例子
| \---hellowarp
|你好-view.fxml
|
\ - -工具
翘曲包装器.exe
获取和安装warp
在项目的根目录中创建一个新目录\tools。
从以下位置下载 Warp:
并将 warp 可执行文件(重命名)复制到以下位置:
\tools\warp-packer.exe
文件
pom.xml
Maven 项目。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>hellowarp</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>17</java.version>
<javafx.version>17.0.1</javafx.version>
</properties>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>${javafx.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<!--compile-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<!--create linked image-->
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.8</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jlink</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>com.example.hellowarp/com.example.hellowarp.HelloWarp</mainClass>
<compress>2</compress>
<noManPages>true</noManPages>
<noHeaderFiles>true</noHeaderFiles>
<stripDebug>true</stripDebug>
<launcher>${project.artifactId}</launcher>
</configuration>
</plugin>
<!--package image as an exe-->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
<configuration>
<!-- obtain warp-packer.exe from: "https://github.com/dgiagio/warp/releases/download/v0.3.0/windows-x64.warp-packer.exe" -->
<executable>${project.basedir}\tools\warp-packer.exe</executable>
<arguments>
<argument>--arch</argument>
<argument>windows-x64</argument>
<argument>--input_dir</argument>
<argument>${project.build.directory}\image</argument>
<argument>--exec</argument>
<argument>bin\${project.artifactId}.bat</argument>
<argument>--output</argument>
<argument>${project.build.directory}\${project.artifactId}.exe</argument>
</arguments>
</configuration>
</plugin>
</plugins>
</build>
</project>
模块信息.java
应用程序的 Java 平台模块定义。
module com.example.hellowarp {
requires javafx.controls;
requires javafx.fxml;
opens com.example.hellowarp to javafx.fxml;
exports com.example.hellowarp;
}
HelloWarp.java
JavaFX 应用程序。
package com.example.hellowarp;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class HelloWarp extends Application {
@Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(
HelloWarp.class.getResource(
"hello-view.fxml"
)
);
Scene scene = new Scene(fxmlLoader.load());
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
HelloController.java
JavaFX FXML 控制器类。
package com.example.hellowarp;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class HelloController {
@FXML
private Label welcomeText;
@FXML
protected void onHelloButtonClick() {
welcomeText.setText("Welcome to my JavaFX Application!");
}
}
HelloView.fxml
UI 视图定义文件。
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Button?>
<VBox alignment="CENTER" spacing="20.0" prefWidth="250" xmlns:fx="http://javafx.com/fxml"
fx:controller="com.example.hellowarp.HelloController">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
</padding>
<Label fx:id="welcomeText"/>
<Button text="Hello!" onAction="#onHelloButtonClick"/>
</VBox>
正在运行的hellowarp.exe 应用程序的屏幕截图
常见问题
常见问题部分仅提供上下文信息。如果您已经知道此信息或不需要此信息,则可以忽略此部分。
这是“胖罐”的替代分发方法吗?
是的,我想是的。
这适合什么?
在您知道用户正在运行 Windows 的环境中分发的小型应用程序。
我还可以将我的应用程序打包为 MSI 安装程序吗?
是的。我使用akman jpackage-maven-plugin 做到了这一点,效果很好。为了限制大小和范围,我不会在这个答案中记录。
使用“git repo”、“fat jar”、exe、打包的安装程序或“压缩”图像会更好吗?
这取决于你在做什么。
如果您的目标是其他开发人员,请在 github 上设置一个帐户,将您的项目放在那里,并为开发人员提供一个 maven 或 gradle 构建文件,以便在他们的环境中从源代码构建应用程序。只需将应用程序打包为标准 jar 文件(没有胖 jar)就可以了。您创建的任何 jar 都可以部署到 maven Central。为 jar 使用 module-info.java,以便它可以通过 jlink 链接到打包的应用程序中。
如果是学校项目。这取决于学校的录取偏好。也许他们只是想要 git 中的代码源,而这就是您所需要的。或者,您可以创建一个您提供的(精简)jar 文件(或带有 jar 及其依赖项的 zip),因为标准学校系统上已经安装了所有相关的 Java/JavaFX 软件。
或者它可能是一个已知的操作系统环境,例如Windows、Mac 或 Linux,您可以使用 jpackage 为其中一个(或两个)环境构建可安装包。
或者,如果它只是 Windows,这种打包为“exe”的解决方案可能会很好地工作。
JavaFX 开发不支持"fat jar" configuration。我不推荐它。您可以(目前)让它工作,有时它可能很方便,但您需要决定这样做所涉及的权衡是否值得。
如果您正在构建商业或流行的开源桌面产品,请使用适合您目标平台(例如 msi 或Windows 的 exe,Linux 的 rpm 和 deb,mac 的 dmg)。您可以选择将这些打包格式部署到 Windows 或 Mac 应用商店或 Linux yum/apt 存储库。
如果是移动部署,请使用gluon mobile。
我可以使用 Apache jlink Maven 插件代替 OpenJFX Maven 插件吗?
此时OpenJFX JavaFX Maven plugin 或akman jpackage-maven-plugin 将生成正确的图像。
Apache jlink Maven plugin 目前将失败(使用 JavaFX 17.0.1 和 Apache jlink 插件 3.1.0)。
当我尝试使用 Apache jlink Maven 插件时,它被 JavaFX 平台模块定义弄糊涂了。 Apache jlink 插件开始使用奇怪的幻像模块名称,例如 javafx.graphicsEmpty,它被视为自动模块并传递给 jlink,因此 jlink 拒绝链接它们。我找不到解决这个问题的方法。
当我双击exe时,除了我的应用程序窗口外,标题栏中还有一个名为exe的空白窗口。
是的。取决于应用程序,这可能是一个小麻烦或一个表演障碍。
双击时显示黑屏正是此解决方案的工作方式,如此处所述。
可能有一种方法可以规避这种情况,但我没有对此进行大量调查。您可以查看此处提供的信息(其中讨论了在 MS Windows 中隐藏或最小化应用启动器窗口的各种方法),看看它是否对您有帮助:
如果您不是双击应用程序,而是通过在命令控制台中输入 exe 名称来运行应用程序,则不会出现额外的屏幕,因为您正在输入的现有控制台已经存在。
我可以使用这种技术为其他操作系统创建单文件可执行文件吗?
是的,我相信是的。
目前,除了 Windows 可执行文件,我还没有尝试将此解决方案用于其他任何东西。
warp-packer 能够为各种操作系统生成可执行文件。
要为非 Windows 机器打包,您需要为目标操作系统输入适当的 jlink 图像输出到 warp-packer,然后运行适当的 warp-packer 实用程序(我相信在目标操作系统上)生成单个可执行文件以在该目标操作系统上执行。
如果有兴趣,请参阅 warp-packer 和 jlink 文档。
生成的可执行文件的大小是多少?
对于示例应用程序,我生成的应用程序可执行文件大小为 34 MB。
什么是启动时间?
我没有测量它,但启动时间(双击 exe 后显示应用程序 GUI 窗口的时间)似乎大约是一秒。
我可以为非模块化 Java 项目创建一个 exe。
可能是的,但这超出了我准备在这里讨论的范围,而且方法与这里描述的不同。