JavaFX打包后运行报错‘child process exited with code 1’求助
JavaFX打包exe后启动报错:child process exited with code 1(含UCanAccess数据库与线程)
IDE中运行包含UCanAccess数据库操作和线程逻辑的JavaFX应用一切正常,但用jpackage打包成Windows exe后启动,出现child process exited with code 1报错,无法运行。测试过无数据库和线程的简单JavaFX程序打包后可正常运行,问题大概率出在数据库或线程相关逻辑上。
相关代码与命令
控制器类代码
@Override public void initialize(URL url, ResourceBundle rb) { Calendar calendar = new GregorianCalendar(); Date toSql = calendar.getTime(); lblCopy.setText("Copyright \u00a9"+df.format(toSql)+",All right reserved."); try { splashView(); } catch (InterruptedException | SQLException | IOException ex) { Logger.getLogger(SplashController.class.getName()).log(Level.SEVERE, null, ex); } } //threads for the splash screen public void splashView() throws InterruptedException, SQLException, IOException{ //doTasks(); } //using the task class implementation public void doTasks() throws InterruptedException, SQLException, IOException{ createEnvironmentOnStart(); verifyDatabase(); TaskExecuter tasker = new TaskExecuter(); Thread thread = new Thread(tasker); thread.isDaemon(); thread.start(); progBar.progressProperty().bind(tasker.progressProperty()); progressLoading.textProperty().bind(tasker.messageProperty()); tasker.setOnSucceeded((WorkerStateEvent event) -> { // create a new thread to run another task Thread thread1 = new Thread(() -> { //executeMain(); Platform.exit(); }); thread1.start(); }); } // loading the main App dashboard. public void executeMain(){ Platform.runLater(() -> { try { Parent root =FXMLLoader.load(getClass().getResource("/SalesDataSheets/sds.fxml")); Scene scene = new Scene(root); Stage stage = new Stage(); stage.setScene(scene); stage.setTitle("SalesDataSheets\u2122"); stage.getIcons().add(new Image(SalesDataSheets.class.getResourceAsStream("images/sdslogo.png"))); stage.show(); splashPane.getScene().getWindow().hide(); } catch (IOException ex) { Logger.getLogger(SplashController.class.getName()).log(Level.SEVERE, null, ex); } }); } //checking for expiredate and out of stock goods. public void verifyDatabase() throws SQLException{ Calendar calendar = new GregorianCalendar(); Date toSql = calendar.getTime(); TreeMap<String,Date> expireNote = new TreeMap<>(); TreeMap<String,Integer> outstokeNote = new TreeMap<>(); msgToMsg = new ArrayList<>(); con = DriverManager.getConnection(newDB); state = con.createStatement(); rs = state.executeQuery(sql2); while(rs.next()){ String prod = rs.getString("product"); int stoke = Integer.parseInt(rs.getString("stoke")); Date date = rs.getDate("Expire_Date"); if(date != null){ expireNote.put(prod, date); }else{ expireNote.put(prod, null); } outstokeNote.put(prod, stoke); } Set<Map.Entry<String,Date>> me = expireNote.entrySet(); Set<Map.Entry<String,Integer>> mu = outstokeNote.entrySet(); for(Map.Entry<String,Date> element : me){ String prod = element.getKey(); Date date = element.getValue(); long interval =(date.getTime()-toSql.getTime())/consta; if(interval > 30){ System.out.println("The product "+prod+" is still at safe zone"); }else if((interval>=0) && (interval<30)){ System.out.println("The product " +prod+" is about "+interval+" days to expire"); String nearlyExp = "The product " +prod+" is about "+interval+" days to expire"; msgToMsg.add(nearlyExp); }else{ System.out.println("The product " +prod+" has expired for about "+interval+" days now,please remove it from the DataBase"); String Expired = "The product " +prod+" has expired for about "+(-1*interval)+" days now,please remove it from the DataBase"; msgToMsg.add(Expired); } System.out.println(interval); } for(Map.Entry<String,Integer> element : mu){ String prod = element.getKey(); int stoke = element.getValue(); if(stoke>50){ System.out.println("the item is still available"); }else if((stoke>0)&&(stoke<50)){ System.out.println("The product "+prod+" is about to be out of stoke, for now only "+stoke+" items has remained"); String nearlyOut = "The product "+prod+" is about to be out of stoke, for now only "+stoke+" items has remained"; msgToMsg.add(nearlyOut); }else if(stoke ==0){ System.out.println("The product "+prod+" is OUT OF STOKE"); String Out = "The product "+prod+" is OUT OF STOKE"; msgToMsg.add(Out); } } System.out.println(msgToMsg); } public void createEnvironmentOnStart() throws IOException, SQLException{ //here all the files that are associated with the program resource and storage are created on first installation String dirPath = System.getProperty("user.home") +"\\Documents\\SalesDataSheets"; String maker = "CREATE TABLE Table1 (ID AUTOINCREMENT PRIMARY KEY,product TEXT(255),purchase CURRENCY,sales CURRENCY,stoke INTEGER,ExpireDate DATETIME)"; String maker2 = "CREATE TABLE Table3 (ID AUTOINCREMENT PRIMARY KEY,Product TEXT(255),Units INTEGER)"; String maker3 = "CREATE TABLE Table4 (ID AUTOINCREMENT PRIMARY KEY,Expense TEXT(255),ExpenseAmount CURRENCY)"; String maker4 = "CREATE TABLE Table5 (ID AUTOINCREMENT PRIMARY KEY,essuedAt DATETIME,totalSales CURRENCY,grossProfit CURRENCY,totalExpense CURRENCY,profloss CURRENCY)"; Directory = new File(dirPath); dataBaseSub = new File(dirPath+"\\DataBase"); filesSub = new File(dirPath+"\\SalesDatum"); database = new File(dataBaseSub.getAbsolutePath()+"\\SalesDataDB.accdb"); if(Directory.mkdirs()){ boolean b1 = dataBaseSub.mkdirs(); boolean b2 = filesSub.mkdirs(); System.out.println("the file system created : "+b1+" : "+b2); try (Connection conn = DriverManager.getConnection(newDB+";newdatabaseversion=V2010")) { DatabaseMetaData dmd = conn.getMetaData(); try (ResultSet rs1 = dmd.getTables(null, null, "Clients", new String[] { "TABLE" })) { if (rs1.next()) { System.out.println("Table [Clients] already exists."); } else { System.out.println("Table [Clients] does not exist."); try (Statement s = conn.createStatement()) { s.executeUpdate(maker); System.out.println("table 1 create successively"); s.executeUpdate(maker2); System.out.println("table 3 create successively"); s.executeUpdate(maker3); System.out.println("table 4 create successively"); s.executeUpdate(maker4); System.out.println("table 5 create successively"); } } } } }else{ System.out.println("the file system is adequate"); System.out.println(System.getProperty("user.home")); } }
TaskExecuter类代码
public class TaskExecuter extends Task<Integer> { @Override protected Integer call() throws Exception { int state = 0; for(int i=0;i<=100;i++){ state = state +i; updateValue(state); updateProgress(i,100); updateMessage("Loading..."+String.valueOf(i)+"%"); Thread.sleep(100); } return state; } }
jpackage命令
jpackage --type exe --input . --dest . --main-class SalesDataSheets.SalesDataSheets --main-jar .\SalesDataSheets1.jar --module-path "C:\Program Files\Java\javafx-jmods-18.0.1" --add-modules javafx.controls,javafx.fxml --icon "E:\Netbeans\SalesDataSheet\sdsIcon.ico" --win-shortcut
问题排查与解决方案
1. 线程逻辑错误
- 问题点:代码中
thread.isDaemon();只是获取守护线程状态,并未设置线程为守护线程,非守护线程可能在程序启动时导致资源占用异常。 - 修复:将该行改为:
thread.setDaemon(true); // 设置为守护线程,避免阻止程序正常启动/退出
- 额外优化:
verifyDatabase和createEnvironmentOnStart是耗时的IO/数据库操作,当前在JavaFX初始化线程(UI线程)中直接调用,可能导致UI阻塞甚至启动失败。应将这些操作放到TaskExecuter的call方法中,在后台线程执行:
// 修改TaskExecuter的call方法 @Override protected Integer call() throws Exception { int state = 0; // 先执行数据库初始化与校验 createEnvironmentOnStart(); verifyDatabase(); // 再执行进度更新 for(int i=0;i<=100;i++){ state = state +i; updateValue(state); updateProgress(i,100); updateMessage("Loading..."+String.valueOf(i)+"%"); Thread.sleep(100); } return state; }
同时修改doTasks方法,移除直接调用这两个方法的代码,避免UI线程阻塞。
2. UCanAccess依赖缺失
- 问题点:打包时未将UCanAccess及其依赖(如jackcess、commons-lang3、commons-logging等)包含到程序运行环境中,导致运行时找不到JDBC驱动。
- 修复:
- 确保主jar(
SalesDataSheets1.jar)是包含所有依赖的fat jar(可通过Maven Shade或Gradle Shadow插件打包)。 - 若未打包成fat jar,需将UCanAccess相关依赖jar放到jpackage命令的
--input目录下,确保打包时被包含到runtime中。 - 检查
newDB变量的JDBC URL是否正确,尤其是数据库文件路径,避免使用相对路径导致打包后找不到数据库文件。
- 确保主jar(
3. 权限与路径问题
- 问题点:程序尝试在用户
Documents目录下创建文件/目录,打包后的exe可能因权限不足无法访问该路径,或者路径拼接存在转义问题。 - 修复:
- 测试时可添加
--win-console参数到jpackage命令,启动exe时弹出控制台,查看具体的异常日志:
- 测试时可添加
jpackage --type exe --input . --dest . --main-class SalesDataSheets.SalesDataSheets --main-jar .\SalesDataSheets1.jar --module-path "C:\Program Files\Java\javafx-jmods-18.0.1" --add-modules javafx.controls,javafx.fxml --icon "E:\Netbeans\SalesDataSheet\sdsIcon.ico" --win-shortcut --win-console
- 确认
System.getProperty("user.home")返回的路径正确,可在控制台打印路径验证。 - 若存在权限问题,可尝试右键exe选择"以管理员身份运行"测试,或修改程序目录到无需管理员权限的位置。
4. JavaFX线程规则遵守
- 问题点:
tasker.setOnSucceeded中启动的线程直接调用Platform.exit()可能存在线程安全问题,违反JavaFX线程规则。 - 修复:将
Platform.exit()放到Platform.runLater中执行:
tasker.setOnSucceeded((WorkerStateEvent event) -> { Platform.runLater(() -> { // executeMain(); Platform.exit(); }); });
内容的提问来源于stack exchange,提问作者Paul_m
相关产品推荐
相关产品推荐

