前景:将javafx打包成exe应用程序,并可实现在线更新。个人记录
首次接触javafx,
打成exe的方式有很多种(注意是模块化项目,还是非模块化的。 jdk9的项目 支持模块化,项目中仅有一个module-info )官方写了很多,但实现起来,总有毛病。项目使用maven,只看了maven的,Gradle 不搞
- 打成jar,用exe4j 将环境打进exe
- jdk1.8的 Javapackage
- jdk14+的 jpackager (模块化/非模块化,本文实现的是非模块化的)
经多次尝试,最后试出来第三种 成功。
非模块化的,需要用一个类去启动,main-class不能是extends Application的文件
/**
* @Description: 非模块化程序,得采用这种方式去启动 主程序。 可以正常打出exe启动。
* @since: 2021/10/23 10:38
*/
public class Launch {
public static void main(String[] args) {
Application.launch(Main.class, args);
}
}
最终效果
- 安装包
- 执行安装程序
- 添加到了桌面快捷
文件夹内容
打包操作,先打成jar,再命令打成exe
使用maven插件maven-shade-plugin(生成与依赖包一起打的.jar包),jdk14+ jpackage(打成.exe)
部分代码在文章最后
1. clean,package 将依赖一起打包成 xx-shade.jar(名称pom里指定)
这里包名,前缀包名+版本号(1.0.0)。程序更新后 将以这个格式修改.cfg中配置 (例如:xx-shade_1.0.0.jar)
.cfg 指定运行jar包。
第一个版本的包名必须是程序中设定的格式 Upgrader.JAR_NAME_PREFIX,否则首次更新无法成功修改(根据整个包名 做替换,不是重写配置)
.cfg 配置文件内容
[Application]
app.classpath=$APPDIR\peg-1.0-SNAPSHOT-shade_1.0.6.jar
app.mainclass=org.dy.Launch
[JavaOptions]
java-options=-Djpackage.app-version=1.0
版本号:不能大于9,大于9的,自行进1。计算的版本号码方式是 去掉.的百分数(1.0.1 -> 101,1.1.2 -> 112)
2. 将生成的jar,复制一份到lib文件夹(任意)下
修改包名,与项目内设定的一致。后续更新jar包名也需一致
3. 可设置exe图标,一并放在以上文件夹lib下,再添加命令 --icon lib\图标.ico
4. 通过jdk14+ 带的工具jpackage进行打包。
命令:jpackage --name peg --input lib --main-class org.dy.Launch --main-jar peg-1.0-SNAPSHOT-shade.jar --icon lib\logo.ico --vendor dyjx --win-dir-chooser --win-shortcut --win-menu --win-menu-group "packaging"
--name 安装exe的名称
--input 要打包的路径
--main-class 启动类
--icon exe图标,这里格式win得使用ico的文件,不然打不出来包。路径也得在input下。
--main-jar lib里面的jar,打包的时候也是指定的--main-class 所指的类
--vendor 安装后应用程序的作者,控制面板查看
--win-dir-chooser 安装时添加 “选择安装路路径"
--win-shortcut 安装后自动在桌面添加快捷键
--win-menu 添加到系统菜单中
--win-menu-group "packaging" 如果没有–win-menu 会报 311 错误
更多,cmd 中使用 jpackage -h 查看命令操作(可以不使用安装程序,直接生成)
版本号计算方法,去掉.的百分数(1.0.1 -> 101,1.1.2 -> 112)
private static int versionToInt(String version) {
String[] items = version.split("\\.");
int versionInt = 0;
for (String item : items) {
versionInt *= 10;
versionInt += Integer.parseInt(item.trim());
}
return versionInt;
}
修改配置
/**
* 修改配置
*/
public void writeVersionInfo() {
String rootPath = JarUtil.getRootPath();
File file = new File(rootPath + "/xx.cfg");
List<String> versionInfo = new ArrayList<>();
if (file.isFile() && file.exists()) {
try {
FileInputStream fileInputStream = new FileInputStream(file);
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String text = "";
while ((text = bufferedReader.readLine()) != null) {
versionInfo.add(text);
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 写回去
FileWriter fw = null;
try {
fw = new FileWriter(file);
for (String s : versionInfo) {
if (s.indexOf("app.classpath=$APPDIR\\" + Upgrader.JAR_NAME_PREFIX + Upgrader.CURRENT_VERSION +".jar") != -1) {
fw.write("app.classpath=$APPDIR\\" + Upgrader.JAR_NAME_PREFIX + Upgrader.NEW_VERSION + ".jar\n");
continue;
}
fw.write(s + "\n");
}
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
JarUtil
/**
* 获取项目根文件夹目录
* @return
*/
public static String getRootPath() {
String path = JarUtil.class.getProtectionDomain().getCodeSource().getLocation().getPath();
if (!path.isBlank()) {
if (System.getProperty("os.name").contains("dows")) {
path = path.substring(1);
}
if (path.contains("jar")) {
path = path.substring(0, path.lastIndexOf("."));
return path.substring(0, path.lastIndexOf("/"));
}
}
return path.replace("target/classes/", "");
}
pom.xml
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.fxml</include>
<include>**/*.css</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<release>${maven.compiler.release}</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<shadedArtifactAttached>true</shadedArtifactAttached>
<shadedClassifierName>project-classifier</shadedClassifierName>
<outputFile>target/${project.artifactId}-${project.version}-shade.jar</outputFile>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.dy.Launch</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>