4.5 KiB
4.5 KiB
单实例问题最终修复说明
问题描述
编译后的 exe 文件可以打开多个实例,无法实现单实例启动。
问题原因分析
- 应用标识符不一致:package.json 中的 name 和 electron-builder 配置中的 appId 不一致
- 单实例检查逻辑过于复杂:使用了自定义的锁文件管理,而不是直接使用 Electron 内置的单实例机制
- 多个进程残留:之前的实例异常退出时,进程没有正确清理,导致锁文件失效
修复内容
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() 方法:
/**
* 单应用启动实现
* 请求单一实例锁,
* 如果该方法返回`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 内置的单实例机制
- 保留锁文件作为备份,但不依赖它进行单实例检查
修复后的功能特点
标准单实例行为
- 首次启动:应用正常启动,显示主窗口
- 重复启动:检测到已有实例,自动退出新实例,激活现有实例窗口
- 窗口激活:如果现有窗口最小化,自动恢复并聚焦
跨平台兼容
- Windows: 使用系统级的单实例锁
- macOS: 使用系统级的单实例锁
- Linux: 使用系统级的单实例锁
异常处理
- 应用崩溃时自动清理锁文件
- 监听各种退出信号确保清理
- 提供手动清理功能
使用方法
开发环境测试
# 启动第一个实例
npm run dev
# 尝试启动第二个实例(应该被阻止)
npm run dev
生产环境测试
# 构建应用
npm run build:win
# 安装并运行
# 尝试多次点击 exe 文件,应该只能启动一个实例
清理工具
# 清理锁文件
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. 生产环境
- 确保应用正常退出时能清理资源
- 监控应用的单实例行为
总结
通过统一应用标识符、简化单实例实现逻辑、清理残留进程,成功解决了编译后可以打开多个实例的问题。现在应用严格按照单实例模式运行,符合桌面应用的标准行为。