You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何限制Linux平台下的Flutter桌面应用仅运行单个实例?

Flutter Linux 应用单实例实现方案

方法一:基于D-Bus会话总线(推荐,符合Linux桌面规范)

Linux桌面环境普遍使用D-Bus实现进程间通信,通过注册唯一的D-Bus名称可以确保单实例运行,还能激活已有实例的窗口。

  1. 添加依赖到pubspec.yaml:
dependencies:
  dbus: ^0.7.8
  1. 实现单实例逻辑:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 11:01:27