问题描述
我想在 javafx 中创建一个应用程序。我是 java fx 和 gradle 的新手,所以目前我只是在处理一些事情。我想尝试的第一件事是为可执行的 jar 或 exe 构建一个应用程序。我已经尝试了几件事,但我从来没有得到一个简单的 jar 来执行。当我双击它时它不会执行。当我尝试从命令行执行它时,我收到此错误:错误:缺少 JavaFX 运行时组件,需要运行此应用程序 我尝试了很多在网上找到的解决方案,但都没有成功。
这是应用程序的代码:
package org.example;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
Label label = new Label("hello world");
Scene scene = new Scene(label);
primaryStage.setScene(scene);
primaryStage.show();
}
}
这是我的 build.gradle 文件:
plugins {
id 'java'
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.9'
}
group 'org.example'
version ''
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
}
javafx {
version = "15.0.1"
modules = [ 'javafx.controls','javafx.fxml' ]
}
mainClassName = 'org.example.Main'
jar {
manifest {
attributes 'Main-Class': 'org.example.Main'
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
有人知道如何解决这个问题吗?
解决方法
使用 jlink
构建包含 JavaFX 模块的 Java 运行时。
使用 jpackage
(来自 JDK 14 及更高版本)使用该运行时捆绑您的应用程序。这将为您提供 .exe 启动器和特定于平台的安装程序。
Gradle 'Exec' 任务可以在构建过程中轻松为您运行这两种工具。
为了简化操作,您可以考虑使用包含 JavaFX 模块的 JDK,这样您就不必费心配置 JavaFX SDK 和模块路径。 来自 Azul 和 Bellsoft 的 OpenJDK 版本包含 JavaFX。
这是我与 jpackage 一起使用的任务类型的示例。我的“appImage”任务复制文件以准备捆绑,我的 jlink 任务创建运行时映像。此任务创建一个可运行的应用程序映像,我将其用于第二次调用 jpackage 以生成安装程序。如果您不需要安装程序,也可以直接压缩此映像,但每个映像都是特定于平台的,因为它包含要使用的本机启动器和 JRE:
task jpackageImage(type: Exec,dependsOn: [jlink,appImage]) {
workingDir = project.projectDir
inputs.property('consoleApp',project.consoleApp)
inputs.property('vendorName',project.vendorName)
if (project.hasProperty('copyright')) {
inputs.property('copyright',project.copyright)
}
// TODO set input directory
inputs.dir "${buildDir}${File.separator}image${File.separator}app"
inputs.dir "${buildDir}${File.separator}image${File.separator}runtime"
// define outputs
outputs.dir "${buildDir}${File.separator}application"
// in a doFirst in case values change (e.g. archive name gets version bump)
// after configuration phase
doFirst {
// Error: Application output directory XXXXXXXXXXXXXX already exists.
def tmpRoot = "$buildDir/tmp/image"
project.delete tmpRoot
project.delete "${buildDir}${File.separator}application"
// resource directories need to exist
project.mkdir resourceDir
//file("${buildDir}/image/app/README.txt").text = "This file should be installed."
def appName = project.applicationName // project.applicationName.replaceAll(" ","")
def copyrightStr = project.hasProperty('copyright') ? project.copyright.toString() : "Copyright (c) ${year} ${vendorName}".toString().trim()
def tmp = [
"${jpackageTool}",'--type','app-image',// Valid values on Windows are: {"app-image","exe","msi"}
'--verbose','--temp',tmpRoot,'--app-version',project.version,'--input',"${buildDir}${File.separator}image${File.separator}app",'--runtime-image',"${buildDir}${File.separator}image${File.separator}runtime",'--name',appName,'--main-jar',"libs${File.separator}${configurations.runtime.artifacts.files.singleFile.name}",'--main-class',mainClass,'--resource-dir',resourceDir,'--icon',iconFileStr,'--description',project.description,'--vendor',vendorName,//'--category','Utility','--copyright',copyrightStr,'--dest',"${buildDir}${File.separator}application",]
// Use a console app for easier debugging (log messages/debug prints are visible)
if (osName.startsWith('windows')) {
// Windows-specific options
//'--win-menu',//'--win-menu-group',//'--win-upgrade-uuid',project.upgradeUUID,//'--win-shortcut',// for a console application
if (project.consoleApp) {
tmp.addAll(['--win-console'])
}
}
if (osName.startsWith('mac')) {
tmp.addAll([
// macOS-specific options
//This name must be less than 16 characters long and be suitable for displaying in the menu bar and the application Info window.
'--mac-package-name',project.macPkgName,'--mac-package-identifier',project.macPkgIndentifier,//'--mac-package-signing-prefix',<prefix string>,//'--mac-sign',// Request that the bundle be signed
//'--mac-signing-keychain',<file path>,//'--mac-signing-key-user-name','<team name>'
])
}
commandLine = tmp
println commandLine
}
// workaround https://bugs.openjdk.java.net/browse/JDK-8254920
doLast {
if (osName.startsWith('windows') && jlinkCompression == 2) {
project.copy {
from "${buildDir}\\application\\${project.applicationName}\\runtime\\bin\\zip.dll"
into "${buildDir}\\application\\${project.applicationName}"
}
}
}
}
我真的应该在某个时候为这些东西制作一个合适的 Gradle 插件。
您可以在 https://docs.oracle.com/en/java/javase/14/docs/specs/man/jpackage.html
了解有关 jpackage 的更多信息 ,如果您想对像您这样的示例进行短期、有些不稳定的修复,您可以在与当前类相同的类路径中使用另一个类,并使用 main 方法。您将在该方法的主体中执行的操作是 Main.main(args)。这将诱使程序不认为它是 Java 应用程序,从而使其正常工作。此外,您必须将主类名称更改为新类的名称。