Skip to content

Commit ce97ec7

Browse files
committed
更新了一堆东西,对1.6.0的发布做最后准备
- 移除启动脚本中的警告提示窗口,改为静默退出重复实例 - 添加WatchService监听重复启动信号文件,实现窗口激活功能 - 创建HomeworkCheckerLauncher.vbs启动器脚本 - 更新JavaFX版本依赖至25.0.1 - 添加单例管理器全局引用和激活信号处理逻辑 - 在重复启动时创建信号文件并由首个实例处理窗口激活 - 添加文件系统监听服务用于检测激活信号 - 更新版本号至1.6.0-beta - 修改更新日志内容,增加新功能说明和依赖更新信息
1 parent 1e9a7f3 commit ce97ec7

10 files changed

Lines changed: 1568 additions & 21 deletions

File tree

HomeworkChecker.aip

Lines changed: 1393 additions & 0 deletions
Large diffs are not rendered by default.

HomeworkCheckerLauncher.vbs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Set objShell = CreateObject("Wscript.Shell")
2+
objShell.CurrentDirectory = CreateObject("Scripting.FileSystemObject").GetParentFolderName(WScript.ScriptFullName)
3+
objShell.Run "cmd /c app.bat", 0, True

app.bat

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
11
@echo off
22

3-
java --module-path "javafx-sdk-25.0.1\lib" --add-modules javafx.controls,javafx.fxml --enable-native-access=javafx.graphics --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED -jar "homeworkChecker-1.5.0 - snapshot-shaded.jar"
4-
5-
pause
3+
"jre64\bin\java.exe" --module-path "javafx-sdk-21.0.9\lib" --add-modules javafx.controls,javafx.fxml -jar "homeworkChecker-1.4.2-beta-shaded.jar"

logo.ico

66.1 KB
Binary file not shown.

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>com.xfty</groupId>
88
<artifactId>homeworkChecker</artifactId>
9-
<version>1.5.0-snapshot</version>
9+
<version>1.6.0-beta</version>
1010
<name>homeworkChecker</name>
1111

1212
<properties>

src/main/java/com/xfty/homeworkchecker/Entry.java

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,20 @@
44
import com.xfty.homeworkchecker.service.FileInitManager;
55
import com.xfty.homeworkchecker.service.SingletonInstanceManager;
66
import javafx.application.Application;
7+
import javafx.application.Platform;
78
import javafx.fxml.FXMLLoader;
89
import javafx.scene.Scene;
910
import javafx.scene.control.Alert;
1011
import javafx.scene.image.Image;
1112
import javafx.stage.Stage;
13+
import org.apache.commons.io.FileUtils;
1214
import org.slf4j.Logger;
1315
import org.slf4j.LoggerFactory;
1416

17+
import java.io.File;
1518
import java.io.IOException;
19+
import java.nio.file.Files;
20+
import java.nio.file.Paths;
1621
import java.time.LocalDate;
1722
import java.util.Locale;
1823
import java.util.Objects;
@@ -47,18 +52,9 @@ public void start(Stage stage) throws IOException {
4752

4853
// 初始化进程前检查是否已经运行了实例
4954
if (!initSingletonCheck()) {
50-
// 已经有一个实例在运行,退出当前实例
51-
logger.warn("Another instance is already running. Exiting...");
52-
53-
Alert alert = new Alert(Alert.AlertType.WARNING);
54-
alert.setTitle(Idf.userLanguageBundle.getString("entry.title.notice"));
55-
alert.setHeaderText(Idf.userLanguageBundle.getString("entry.header.notice"));
56-
alert.setContentText(Idf.userLanguageBundle.getString("entry.content.alreadyRunning"));
57-
// 设置窗口置顶
58-
alert.getDialogPane().getScene().getWindow().requestFocus();
59-
alert.showAndWait();
60-
61-
System.exit(1);
55+
// 已经有一个实例在运行,退出当前实例(无提示)
56+
logger.warn("Another instance is already running. Exiting silently...");
57+
System.exit(0);
6258
return;
6359
}
6460

@@ -88,6 +84,11 @@ public void start(Stage stage) throws IOException {
8884
stage.setScene(scene);
8985
stage.getIcons().add(new Image(Objects.requireNonNull(Entry.class.getResourceAsStream("icon/logo.png"))));
9086
stage.show();
87+
88+
// 启动WatchService监听激活信号
89+
if (Idf.singletonManager != null) {
90+
Idf.singletonManager.startWatchService(stage);
91+
}
9192

9293
// 获取主页面控制器引用
9394
mainPageController = fxmlLoader.getController();
@@ -119,7 +120,45 @@ public void start(Stage stage) throws IOException {
119120
*/
120121
private boolean initSingletonCheck() {
121122
SingletonInstanceManager singletonManager = new SingletonInstanceManager();
122-
return singletonManager.acquireLock();
123+
boolean acquired = singletonManager.acquireLock();
124+
125+
if (acquired) {
126+
// 第一个实例:保存引用,后续传入 Stage
127+
Idf.singletonManager = singletonManager;
128+
} else {
129+
// 第二个实例:创建信号文件后退出
130+
createRepeatedStartFile();
131+
}
132+
133+
return acquired;
134+
}
135+
136+
/**
137+
* 创建重复启动信号文件
138+
*/
139+
private void createRepeatedStartFile() {
140+
try {
141+
File userDir = FileUtils.getUserDirectory();
142+
File homeworkCheckerDir = new File(userDir, "homeworkChecker");
143+
144+
if (!homeworkCheckerDir.exists()) {
145+
FileUtils.forceMkdir(homeworkCheckerDir);
146+
}
147+
148+
File signalFile = new File(homeworkCheckerDir, "repeatedly.start");
149+
Files.writeString(Paths.get(signalFile.getAbsolutePath()),
150+
String.valueOf(System.currentTimeMillis()));
151+
152+
logger.info("Created activation signal file: {}", signalFile.getAbsolutePath());
153+
154+
// 等待一小段时间确保第一个实例能检测到
155+
Thread.sleep(200);
156+
} catch (IOException | InterruptedException e) {
157+
logger.error("Error creating activation signal file", e);
158+
if (e instanceof InterruptedException) {
159+
Thread.currentThread().interrupt();
160+
}
161+
}
123162
}
124163

125164
private void initProgress() {

src/main/java/com/xfty/homeworkchecker/Idf.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.xfty.homeworkchecker;
22

33
import com.alibaba.fastjson2.JSONObject;
4+
import com.xfty.homeworkchecker.service.SingletonInstanceManager;
45
import javafx.scene.text.Font;
56

67
import java.util.List;
@@ -102,4 +103,7 @@ public class Idf {
102103
public static int iconClickedCount = 0;
103104
public static boolean isAboutIconTriggered = false;
104105
public static boolean isAboutIconTriggeredAgain = false;
106+
107+
// 单例管理器引用
108+
public static SingletonInstanceManager singletonManager;
105109
}

src/main/java/com/xfty/homeworkchecker/service/SingletonInstanceManager.java

Lines changed: 112 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.xfty.homeworkchecker.service;
22

3+
import javafx.application.Platform;
4+
import javafx.stage.Stage;
35
import org.apache.commons.io.FileUtils;
46
import org.slf4j.Logger;
57
import org.slf4j.LoggerFactory;
@@ -8,16 +10,22 @@
810
import java.io.IOException;
911
import java.nio.channels.FileChannel;
1012
import java.nio.channels.FileLock;
11-
import java.nio.file.Paths;
13+
import java.nio.file.*;
1214
import java.nio.file.StandardOpenOption;
1315

1416
public class SingletonInstanceManager {
1517
private static final Logger logger = LoggerFactory.getLogger(SingletonInstanceManager.class);
1618
private static final String LOCK_FILENAME = "progress.lock";
19+
private static final String ACTIVATION_SIGNAL_FILE = "repeatedly.start";
1720

1821
private FileChannel channel;
1922
private FileLock lock;
2023
private File lockFile;
24+
private File homeworkCheckerDir;
25+
26+
private WatchService watchService;
27+
private Thread watchThread;
28+
private Stage mainStage;
2129

2230
/**
2331
* 尝试获取单例锁
@@ -27,7 +35,7 @@ public boolean acquireLock() {
2735
try {
2836
// 获取用户文档目录下的homeworkChecker目录
2937
File userDir = FileUtils.getUserDirectory();
30-
File homeworkCheckerDir = new File(userDir, "homeworkChecker");
38+
homeworkCheckerDir = new File(userDir, "homeworkChecker");
3139

3240
// 确保目录存在
3341
if (!homeworkCheckerDir.exists()) {
@@ -67,10 +75,112 @@ public boolean acquireLock() {
6775
}
6876
}
6977

78+
/**
79+
* 启动WatchService监听激活信号
80+
* @param stage 主窗口引用
81+
*/
82+
public void startWatchService(Stage stage) {
83+
this.mainStage = stage;
84+
85+
try {
86+
watchService = FileSystems.getDefault().newWatchService();
87+
Path path = Paths.get(homeworkCheckerDir.getAbsolutePath());
88+
path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE);
89+
90+
// 启动后台监听线程
91+
watchThread = new Thread(this::watchActivationSignal, "ActivationSignalWatcher");
92+
watchThread.setDaemon(true);
93+
watchThread.start();
94+
95+
logger.info("WatchService started for activation signal monitoring");
96+
} catch (IOException e) {
97+
logger.error("Failed to start WatchService", e);
98+
}
99+
}
100+
101+
/**
102+
* 监听激活信号的后台任务
103+
*/
104+
private void watchActivationSignal() {
105+
while (!Thread.currentThread().isInterrupted()) {
106+
try {
107+
WatchKey key = watchService.take(); // 阻塞等待事件
108+
109+
for (WatchEvent<?> event : key.pollEvents()) {
110+
WatchEvent.Kind<?> kind = event.kind();
111+
112+
if (kind == StandardWatchEventKinds.OVERFLOW) {
113+
continue;
114+
}
115+
116+
// 检查是否是激活信号文件
117+
Path fileName = (Path) event.context();
118+
if (ACTIVATION_SIGNAL_FILE.equals(fileName.toString())) {
119+
logger.info("Received activation signal from another instance");
120+
121+
// 在JavaFX应用线程中激活窗口并短暂置顶
122+
Platform.runLater(() -> {
123+
if (mainStage != null) {
124+
// 先置顶窗口
125+
mainStage.setAlwaysOnTop(true);
126+
mainStage.toFront();
127+
mainStage.requestFocus();
128+
logger.info("Window activated and brought to front with alwaysOnTop");
129+
130+
// 1.5秒后取消置顶
131+
javafx.animation.PauseTransition pause = new javafx.animation.PauseTransition(
132+
javafx.util.Duration.millis(1500)
133+
);
134+
pause.setOnFinished(e -> {
135+
mainStage.setAlwaysOnTop(false);
136+
logger.info("Window alwaysOnTop disabled");
137+
});
138+
pause.play();
139+
}
140+
});
141+
142+
// 删除信号文件
143+
File signalFile = new File(homeworkCheckerDir, ACTIVATION_SIGNAL_FILE);
144+
if (signalFile.exists()) {
145+
signalFile.delete();
146+
logger.info("Activation signal file deleted");
147+
}
148+
}
149+
}
150+
151+
// 重置key以继续接收事件
152+
boolean valid = key.reset();
153+
if (!valid) {
154+
break;
155+
}
156+
} catch (InterruptedException e) {
157+
logger.info("WatchService thread interrupted");
158+
Thread.currentThread().interrupt();
159+
break;
160+
} catch (Exception e) {
161+
logger.error("Error in WatchService", e);
162+
}
163+
}
164+
}
165+
70166
/**
71167
* 释放锁
72168
*/
73169
public void releaseLock() {
170+
// 先关闭WatchService
171+
if (watchThread != null) {
172+
watchThread.interrupt();
173+
}
174+
175+
if (watchService != null) {
176+
try {
177+
watchService.close();
178+
logger.info("WatchService closed");
179+
} catch (IOException e) {
180+
logger.error("Error closing WatchService", e);
181+
}
182+
}
183+
74184
try {
75185
if (lock != null && lock.isValid()) {
76186
lock.release();

src/main/resources/com/xfty/homeworkchecker/fxml/loadHistoryHomework.fxml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<?import javafx.scene.shape.Circle?>
1111
<?import javafx.scene.text.Font?>
1212

13-
<AnchorPane prefHeight="517.0" prefWidth="510.0" style="-fx-background-color: #1e1e1e; -fx-background-radius: 20;" xmlns="http://javafx.com/javafx/21.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.xfty.homeworkchecker.controller.LoadHistoryHomework">
13+
<AnchorPane prefHeight="517.0" prefWidth="510.0" style="-fx-background-color: #1e1e1e; -fx-background-radius: 20;" xmlns="http://javafx.com/javafx/25" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.xfty.homeworkchecker.controller.LoadHistoryHomework">
1414
<children>
1515
<Label alignment="CENTER" layoutY="51.0" prefHeight="28.0" prefWidth="444.0" text="%load.history.title" textFill="WHITE" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0">
1616
<font>

0 commit comments

Comments
 (0)