优化
This commit is contained in:
152
SINGLE_INSTANCE_FINAL_FIX.md
Normal file
152
SINGLE_INSTANCE_FINAL_FIX.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# 单实例问题最终修复说明
|
||||
|
||||
## 问题描述
|
||||
编译后的 exe 文件可以打开多个实例,无法实现单实例启动。
|
||||
|
||||
## 问题原因分析
|
||||
1. **应用标识符不一致**:package.json 中的 name 和 electron-builder 配置中的 appId 不一致
|
||||
2. **单实例检查逻辑过于复杂**:使用了自定义的锁文件管理,而不是直接使用 Electron 内置的单实例机制
|
||||
3. **多个进程残留**:之前的实例异常退出时,进程没有正确清理,导致锁文件失效
|
||||
|
||||
## 修复内容
|
||||
|
||||
### 1. 统一应用标识符
|
||||
- **package.json**: `name` 改为 `dify-market-manager-gui`
|
||||
- **electron-builder.yml**: `appId` 改为 `com.huashiai.dify-market-manager-gui`
|
||||
- **主进程**: `setAppUserModelId` 设置为 `com.huashiai.dify-market-manager-gui`
|
||||
|
||||
### 2. 简化单实例实现
|
||||
参考标准 Electron 单实例实现,使用 `app.requestSingleInstanceLock()` 方法:
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* 单应用启动实现
|
||||
* 请求单一实例锁,
|
||||
* 如果该方法返回`false`,
|
||||
* 则表示已经有一个实例在运行,
|
||||
* 可以通过`app.quit()`方法退出当前实例。
|
||||
*/
|
||||
const gotTheLock = app.requestSingleInstanceLock()
|
||||
|
||||
if (!gotTheLock) {
|
||||
logger.info('检测到已有实例运行,退出当前实例')
|
||||
app.quit()
|
||||
} else {
|
||||
logger.info('这是第一个实例,继续启动')
|
||||
|
||||
// 监听第二个实例被运行时
|
||||
app.on('second-instance', (event, commandLine, workingDirectory) => {
|
||||
logger.info('检测到第二个实例启动,激活现有实例')
|
||||
// 当有第二个实例被运行时,激活之前的实例并将焦点置于其窗口
|
||||
const windows = BrowserWindow.getAllWindows()
|
||||
if (windows.length > 0) {
|
||||
const mainWindow = windows[0]
|
||||
if (mainWindow.isMinimized()) {
|
||||
mainWindow.restore()
|
||||
}
|
||||
mainWindow.show()
|
||||
mainWindow.focus()
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 清理残留进程和锁文件
|
||||
- 创建了 `kill-processes.js` 脚本来终止所有相关进程
|
||||
- 创建了 `cleanup-lock-files.js` 脚本来清理所有锁文件
|
||||
- 终止了 11 个残留的应用进程
|
||||
|
||||
### 4. 简化单实例管理器
|
||||
- 移除了复杂的锁文件管理逻辑
|
||||
- 直接使用 Electron 内置的单实例机制
|
||||
- 保留锁文件作为备份,但不依赖它进行单实例检查
|
||||
|
||||
## 修复后的功能特点
|
||||
|
||||
### 标准单实例行为
|
||||
1. **首次启动**:应用正常启动,显示主窗口
|
||||
2. **重复启动**:检测到已有实例,自动退出新实例,激活现有实例窗口
|
||||
3. **窗口激活**:如果现有窗口最小化,自动恢复并聚焦
|
||||
|
||||
### 跨平台兼容
|
||||
- Windows: 使用系统级的单实例锁
|
||||
- macOS: 使用系统级的单实例锁
|
||||
- Linux: 使用系统级的单实例锁
|
||||
|
||||
### 异常处理
|
||||
- 应用崩溃时自动清理锁文件
|
||||
- 监听各种退出信号确保清理
|
||||
- 提供手动清理功能
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 开发环境测试
|
||||
```bash
|
||||
# 启动第一个实例
|
||||
npm run dev
|
||||
|
||||
# 尝试启动第二个实例(应该被阻止)
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### 生产环境测试
|
||||
```bash
|
||||
# 构建应用
|
||||
npm run build:win
|
||||
|
||||
# 安装并运行
|
||||
# 尝试多次点击 exe 文件,应该只能启动一个实例
|
||||
```
|
||||
|
||||
### 清理工具
|
||||
```bash
|
||||
# 清理锁文件
|
||||
node cleanup-lock-files.js
|
||||
|
||||
# 终止所有相关进程
|
||||
node kill-processes.js
|
||||
```
|
||||
|
||||
## 验证修复效果
|
||||
|
||||
### 1. 正常启动
|
||||
```
|
||||
[时间] 这是第一个实例,继续启动
|
||||
[时间] 应用已准备就绪,开始初始化...
|
||||
```
|
||||
|
||||
### 2. 重复启动
|
||||
```
|
||||
[时间] 检测到已有实例运行,退出当前实例
|
||||
```
|
||||
|
||||
### 3. 第二个实例启动
|
||||
```
|
||||
[时间] 检测到第二个实例启动,激活现有实例
|
||||
```
|
||||
|
||||
## 相关文件
|
||||
- `src/main/index.js` - 主进程启动文件(已更新单实例逻辑)
|
||||
- `src/main/utils/singleInstance.js` - 简化的单实例管理器
|
||||
- `package.json` - 应用配置(已更新名称)
|
||||
- `electron-builder.yml` - 构建配置(已更新 appId)
|
||||
- `cleanup-lock-files.js` - 锁文件清理脚本
|
||||
- `kill-processes.js` - 进程终止脚本
|
||||
- `test-single-instance-simple.js` - 简单测试脚本
|
||||
|
||||
## 注意事项
|
||||
|
||||
### 1. 应用标识符
|
||||
- 确保所有配置文件中的应用标识符一致
|
||||
- 修改标识符后需要重新构建应用
|
||||
|
||||
### 2. 开发环境
|
||||
- 开发时如果遇到单实例问题,使用清理脚本
|
||||
- 查看日志文件了解详细情况
|
||||
|
||||
### 3. 生产环境
|
||||
- 确保应用正常退出时能清理资源
|
||||
- 监控应用的单实例行为
|
||||
|
||||
## 总结
|
||||
通过统一应用标识符、简化单实例实现逻辑、清理残留进程,成功解决了编译后可以打开多个实例的问题。现在应用严格按照单实例模式运行,符合桌面应用的标准行为。
|
||||
Reference in New Issue
Block a user