使用exe4j Wizard和Inno Setup Compiler工具打包Java文件和exe遇到的在其他存在存在jre环境的电脑运行出现bug的解决思路

目录

背景

涉及到的打包工具、串口通讯工具和包

解决


背景


 记录一下在使用Java开发串口调试工具的时候发现,将程序打包成可以在无jre环境运行exe程序的时候,发现的一个问题并且解决。

我遇到的问题是由于使用RXT串口通讯包里面需要复制 rxtxParallel.dll 和 rxtxSerial.dll 到 C:\Program Files\Java\jdk1.8.0_241\jre\bin 下,而使用Inno Setup Compiler工具打包的jre环境就是在C:\Program Files\Java\jdk1.8.0_241\jre这个文件夹。

打包完毕后再无jre环境下能够正常安装程序,但是在电脑本身已经存在jre环境的情况下,程序会默认使用系统已经配置的jre进行程序执行。从而导致打包的程序无法运行,因为找不到rxtxParallel.dll 和 rxtxSerial.dll这俩个可执行文件。

涉及到的打包工具、串口通讯工具和包

以下是我开发的时候参考的帖子和工具

  1. RTX工具和使用详情
  2. Java打包成可运行exe工具和使用详情
  3. 串口模拟工具和串口调试工具和使用详情

解决

当我们在有jre环境,但是jre\bin文件夹下并没有 rxtxParallel.dll 和 rxtxSerial.dll 这俩个可执行文件的时候,会出现以下报错:

java.lang.UnsatisfiedLinkError: no rxtxSerial in java.library.path thrown while loading gnu.io.RXTXCommDriver
Exception in Application init method
java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:389)
	at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: Exception in Application init method
	at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:912)
	at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$155(LauncherImpl.java:182)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.UnsatisfiedLinkError: no rxtxSerial in java.library.path
	at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1867)
	at java.lang.Runtime.loadLibrary0(Runtime.java:870)
	at java.lang.System.loadLibrary(System.java:1122)
	at gnu.io.CommPortIdentifier.<clinit>(CommPortIdentifier.java:123)
	at com.lzq.utils.SerialPortUtils.listPorts(SerialPortUtils.java:17)
	at com.lzq.gui.OperationInterfaceView.init(OperationInterfaceView.java:71)
	at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:841)
	... 2 more
Exception running application com.lzq.gui.OperationInterfaceView

报错的内容显示找不到rxtxSerial可执行文件,这种情况就是加载器没有加载到,但是代码中需要调用rxtxSerial,就会出现报错。

解决的思路是我需要手动将rxtxParallel.dll 和 rxtxSerial.dll俩个可执行文件进行加载,我在程序中添加上此方法:

loadLibrary方法:
    /**
     * 加载指定名称的本地库。
     *
     * @param libraryName 本地库的名称(例如 "rxtxSerial.dll")
     */
    public void loadLibrary(String libraryName) {
        // 获取当前工作目录
        String currentDirectory = System.getProperty("user.dir");
        
        // 构建本地库所在路径(假设在 jre/bin 目录下)
        String libraryPath = currentDirectory + File.separator + "jre" + File.separator + "bin";
        
        // 构建本地库文件的完整路径
        String libraryFullPath = libraryPath + File.separator + libraryName;
        
        // 加载本地库文件
        System.load(libraryFullPath);
    }

并且在主程序入口调用进行加载

        this.loadLibrary("rxtxSerial.dll");
        this.loadLibrary("rxtxParallel.dll");

这样就解决了已经存在jre环境但是运行会出现报错,出现没有加载rxtxParallel.dll 和 rxtxSerial.dll 情况。

但是这样的代码会出现一种情况,就是他会主动去加载rxtxParallel.dll 和 rxtxSerial.dll。但是在默认没有jre环境的情况下,使用的是我们手动打包的jre环境去加载,这个环境是已经存在rxtxParallel.dll 和 rxtxSerial.dll这俩个可执行文件的。

这种情况就会出现重复加载的问题。会出现报错:

java.lang.RuntimeException: Exception in Application init method
	at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:912)
	at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$155(LauncherImpl.java:182)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.UnsatisfiedLinkError: Native Library C:\Program Files (x86)\机器操作工具\jre\bin\rxtxSerial.dll already loaded in another classloader
	at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1907)
	at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1845)
	at java.lang.Runtime.loadLibrary0(Runtime.java:870)
	at java.lang.System.loadLibrary(System.java:1122)
	at gnu.io.CommPortIdentifier.<clinit>(CommPortIdentifier.java:123)
	at com.lzq.utils.SerialPortUtils.listPorts(SerialPortUtils.java:17)
	at com.lzq.gui.OperationInterfaceView.init(OperationInterfaceView.java:75)
	at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:841)
	... 2 more

报错的内容显示由于尝试加载已经在另一个类加载器中加载的本地库文件而引起的。

解决的思路就是,在执行加载之前先做一个判断,判断是否需要手动加载,是已经存在。

isJREAvailable方法:
    /**
     * 检查当前环境是否已安装 Java 运行时环境(JRE)。
     *
     * @return 如果已安装 JRE,则返回 true;否则返回 false。
     */
    public boolean isJREAvailable() {
        // 获取 Java 安装目录路径
        String javaHome = System.getProperty("java.home");

        // 构建 JRE 的 bin 目录路径
        File jreBinDirectory = new File(javaHome, "bin");

        // 检查 bin 目录是否存在
        return jreBinDirectory.exists();
    }
isLibraryLoaded方法:
   /**
     * 判断是否加载了(libraryName)文件
     * @param libraryName
     * @return
     */
    public boolean isLibraryLoaded(String libraryName) {
        // 获取本地库加载路径
        String libraryPath = System.getProperty("java.home");

        // 构建本地库文件的完整路径
        String libraryFilePath = libraryPath + File.separator + "bin" + File.separator + libraryName;

        // 检查本地库文件是否存在
        File libraryFile = new File(libraryFilePath);

        // 如果本地库文件存在,则返回 true(表示库已加载),否则返回 false
        return libraryFile.exists();
    }
修改后的loadLibrary方法:
    /**
     * 加载指定的本地库文件。
     *
     * @param libraryName 要加载的本地库文件名
     */
    public void loadLibrary(String libraryName) {
        // 判断是否有JRE环境和是否已加载指定的库文件
        if (isJREAvailable() && !isLibraryLoaded(libraryName)) {
            // 获取当前工作目录
            String currentDirectory = System.getProperty("user.dir");
            // 构建JRE的bin目录路径
            String libraryPath = currentDirectory + File.separator + "jre" + File.separator + "bin";
            // 构建库文件的完整路径
            String libraryFullPath = libraryPath + File.separator + libraryName;
            // 加载库文件
            System.load(libraryFullPath);
        }
    }

并且在主程序入口调用进行加载

        this.loadLibrary("rxtxSerial.dll");
        this.loadLibrary("rxtxParallel.dll");

至此,就解决了java程序在打包后的exe程序在不同环境下的问题。

  • 24
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值