如何限制Linux平台下的Flutter桌面应用仅运行单个实例?
Flutter Linux 应用单实例实现方案
方法一:基于D-Bus会话总线(推荐,符合Linux桌面规范)
Linux桌面环境普遍使用D-Bus实现进程间通信,通过注册唯一的D-Bus名称可以确保单实例运行,还能激活已有实例的窗口。
- 添加依赖到
pubspec.yaml:
dependencies: dbus: ^0.7.8
- 实现单实例逻辑:
import 'dart:io'; import 'package:dbus/dbus.dart'; import 'package:flutter/material.dart'; Future<void> main() async { final dbusClient = DBusClient.session(); const appBusName = 'org.yourdomain.yourapp'; // 替换为你的唯一域名+应用名 try { // 尝试注册唯一D-Bus名称,失败则说明已有实例 await dbusClient.registerName(appBusName, DBusNameFlag.doNotQueue); runApp(const MyApp()); // 应用退出时释放名称 exitCode.whenComplete(() async { await dbusClient.unregisterName(appBusName); await dbusClient.close(); }); } on DBusException { // 尝试激活已有实例的窗口(可选) try { await dbusClient.callMethod( destination: appBusName, path: '/org/yourdomain/yourapp', interface: 'org.freedesktop.Application', name: 'Activate', values: [DBusDict.stringVariant({})], ); } catch (_) {} print('应用已在运行'); exit(0); } } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp(home: Scaffold(appBar: AppBar(title: const Text('单实例应用')))); } }
方法二:使用文件独占锁(简单通用)
通过创建锁文件并获取独占锁,确保同一时间只有一个进程持有锁,实现单实例。
import 'dart:io'; import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; Future<void> main() async { final appDir = await getApplicationSupportDirectory(); final lockFile = File('${appDir.path}/flutter_app.lock'); RandomAccessFile? lockHandle; try { // 打开文件并获取独占锁 lockHandle = await lockFile.open(mode: FileMode.write); await lockHandle.lock(FileLock.exclusive); runApp(const MyApp()); // 退出时释放锁并删除文件 exitCode.whenComplete(() async { await lockHandle?.unlock(); await lockHandle?.close(); await lockFile.delete(); }); } catch (_) { print('应用已在运行'); exit(0); } } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp(home: Scaffold(appBar: AppBar(title: const Text('单实例应用')))); } }
方法三:基于PID文件检查
通过记录进程ID到文件,启动时检查文件中的PID对应的进程是否存活,判断是否已有实例。
import 'dart:io'; import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; // 检查进程是否存活 Future<bool> isProcessAlive(int pid) async { try { Process.killPid(pid, ProcessSignal.sigcont); // 发送空信号验证进程存在 return true; } catch (_) { return false; } } Future<void> main() async { final appDir = await getApplicationSupportDirectory(); final pidFile = File('${appDir.path}/flutter_app.pid'); if (await pidFile.exists()) { final pidStr = await pidFile.readAsString(); final pid = int.tryParse(pidStr) ?? 0; if (pid > 0 && await isProcessAlive(pid)) { print('应用已在运行'); exit(0); } else { // 清理失效的PID文件 await pidFile.delete(); } } // 写入当前进程PID await pidFile.writeAsString('${pid.toString()}'); runApp(const MyApp()); // 退出时删除PID文件 exitCode.whenComplete(() => pidFile.delete()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp(home: Scaffold(appBar: AppBar(title: const Text('单实例应用')))); } }
注意事项
- D-Bus方案优先推荐,适配GNOME、KDE等主流桌面环境,支持激活已有窗口
- 文件锁方案无需依赖桌面服务,适合轻量或非标准环境的应用
- PID文件方案需处理进程意外崩溃的情况(此时PID文件未被删除,需要校验进程是否真的存活)
内容的提问来源于stack exchange,提问作者Anton
相关产品推荐
相关产品推荐

