Skip to content

Commit a2bd702

Browse files
author
liuchuancong
committed
优化windows
1 parent e83a7e6 commit a2bd702

2 files changed

Lines changed: 97 additions & 58 deletions

File tree

lib/common/global/initialized.dart

Lines changed: 32 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,90 @@
11
import 'dart:io';
2-
import 'dart:ffi';
32
import 'dart:developer';
43
import 'package:get/get.dart';
5-
import 'package:win32/win32.dart';
64
import 'package:pure_live/common/index.dart';
75
import 'package:pure_live/plugins/global.dart';
86
import 'package:hive_ce_flutter/hive_flutter.dart';
97
import 'package:launch_at_startup/launch_at_startup.dart';
10-
import 'package:package_info_plus/package_info_plus.dart';
8+
import 'package:pure_live/common/global/windows_utils.dart';
119
import 'package:pure_live/common/utils/hive_pref_util.dart';
1210
import 'package:pure_live/common/global/platform_utils.dart';
1311
import 'package:pure_live/modules/live_play/player_state.dart';
14-
import 'package:flutter_single_instance/flutter_single_instance.dart';
1512
import 'package:pure_live/common/global/platform/mobile_manager.dart';
1613
import 'package:pure_live/common/global/platform/desktop_manager.dart';
1714
import 'package:pure_live/common/services/bilibili_account_service.dart';
15+
import 'package:package_info_plus/package_info_plus.dart' show PackageInfo;
1816

1917
class AppInitializer {
20-
// 单例实例
2118
static final AppInitializer _instance = AppInitializer._internal();
22-
23-
// 是否已经初始化
2419
bool _isInitialized = false;
2520

26-
// 工厂构造函数,返回单例
27-
factory AppInitializer() {
28-
return _instance;
29-
}
30-
31-
// 私有构造函数
21+
factory AppInitializer() => _instance;
3222
AppInitializer._internal();
3323

34-
// 初始化方法
3524
Future<void> initialize(List<String> args) async {
3625
if (_isInitialized) return;
37-
3826
WidgetsFlutterBinding.ensureInitialized();
3927

40-
// 👇 从启动参数获取实例 ID
4128
String instanceId = getInstanceIdFromArgs(args);
4229

43-
// 👇 每个实例使用独立 Hive 路径
44-
final appDir = await getApplicationDocumentsDirectory();
45-
String path = '${appDir.path}${Platform.pathSeparator}pure_live${Platform.pathSeparator}$instanceId';
46-
if (instanceId.isEmpty) {
47-
path = '${appDir.path}${Platform.pathSeparator}pure_live';
48-
}
4930
if (PlatformUtils.isDesktopNotMac) {
50-
final lockFile = File('$path${Platform.pathSeparator}app_instance.lock');
51-
52-
try {
53-
if (!lockFile.parent.existsSync()) lockFile.parent.createSync(recursive: true);
54-
final raf = lockFile.openSync(mode: FileMode.write);
55-
raf.lockSync();
56-
} catch (e) {
57-
log("检测到实例 [$instanceId] 文件夹已被锁定,正在唤醒已有窗口...");
58-
final hwnd = FindWindow(nullptr, TEXT('纯粹直播'));
59-
if (hwnd != 0) {
60-
if (IsIconic(hwnd) != 0) ShowWindow(hwnd, SW_RESTORE);
61-
SetForegroundWindow(hwnd);
62-
}
31+
if (WindowUtils.wakeUpByProp(instanceId)) {
32+
log("Instance [$instanceId] already running. Waking up and exiting.");
6333
exit(0);
6434
}
6535
}
36+
6637
if (PlatformUtils.isDesktop) {
6738
await DesktopManager.initialize();
6839
} else if (PlatformUtils.isMobile) {
6940
await MobileManager.initialize();
7041
}
7142

43+
final appDir = await getApplicationDocumentsDirectory();
44+
String path =
45+
'${appDir.path}${Platform.pathSeparator}pure_live${instanceId.isNotEmpty ? "${Platform.pathSeparator}$instanceId" : ""}';
46+
7247
PrefUtil.prefs = await SharedPreferences.getInstance();
48+
initService();
49+
7350
try {
7451
await Hive.initFlutter(path);
7552
await HivePrefUtil.init();
7653
} catch (e) {
77-
log(e.toString(), name: 'Hive');
78-
exit(0);
54+
log("Hive Init Error: $e");
7955
}
56+
8057
MediaKit.ensureInitialized();
8158
await SupaBaseManager.getInstance().initial();
8259

8360
if (PlatformUtils.isDesktop) {
8461
await DesktopManager.postInitialize();
62+
Future.delayed(const Duration(milliseconds: 800), () {
63+
WindowUtils.markCurrentWindow(instanceId);
64+
});
65+
}
66+
if (PlatformUtils.isDesktopNotMac) {
67+
// 只有主实例(instanceId 为空)才注册自启,避免多个实例互相覆盖注册表
68+
if (instanceId.isEmpty) {
69+
await _setupLaunchAtStartup();
70+
}
8571
}
8672

8773
initRefresh();
88-
initService();
74+
_isInitialized = true;
75+
}
8976

90-
if (PlatformUtils.isDesktopNotMac) {
91-
if (!await FlutterSingleInstance().isFirstInstance()) {
92-
log("Default instance is already running");
93-
exit(0);
77+
String getInstanceIdFromArgs(List<String> args) {
78+
for (var arg in args) {
79+
if (arg.startsWith('--instance=')) {
80+
var parts = arg.split('=');
81+
return parts.length > 1 ? parts[1] : '';
9482
}
95-
await _setupLaunchAtStartup();
9683
}
97-
_isInitialized = true;
84+
return '';
9885
}
9986

100-
// 提取 launchAtStartup 设置
101-
Future<void> _setupLaunchAtStartup() async {
87+
Future<void> _setupLaunchAtStartup() async {
10288
PackageInfo packageInfo = await PackageInfo.fromPlatform();
10389
launchAtStartup.setup(
10490
appName: packageInfo.appName,
@@ -114,17 +100,6 @@ class AppInitializer {
114100
}
115101
}
116102

117-
// 工具方法:解析 instanceId
118-
String getInstanceIdFromArgs(List<String> args) {
119-
for (var arg in args) {
120-
if (arg.startsWith('--instance=')) {
121-
return arg.split('=')[1];
122-
}
123-
return '';
124-
}
125-
return '';
126-
}
127-
128103
void initService() {
129104
Get.put(SettingsService());
130105
Get.put(AuthController());
@@ -135,6 +110,5 @@ class AppInitializer {
135110
Get.put(GlobalPlayerState());
136111
}
137112

138-
// 检查是否已初始化
139113
bool get isInitialized => _isInitialized;
140114
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import 'dart:io';
2+
import 'dart:ffi';
3+
import 'package:ffi/ffi.dart';
4+
import 'package:win32/win32.dart';
5+
6+
class WindowUtils {
7+
static final String _propName = "PureLive_InstanceID";
8+
9+
/// Corrected NativeCallable for WNDENUMPROC
10+
static bool _findAndWake(int targetId) {
11+
bool found = false;
12+
13+
// The function signature MUST be (int, int) -> int for WNDENUMPROC
14+
final callback = NativeCallable<WNDENUMPROC>.isolateLocal((int hwnd, int lParam) {
15+
final propPtr = _propName.toNativeUtf16();
16+
final val = GetProp(hwnd, propPtr);
17+
free(propPtr);
18+
19+
if (val == targetId && val != 0) {
20+
if (IsWindowVisible(hwnd) != 0) {
21+
ShowWindow(hwnd, SW_RESTORE);
22+
SetForegroundWindow(hwnd);
23+
found = true;
24+
return 0;
25+
}
26+
}
27+
return 1; // Continue
28+
}, exceptionalReturn: 0);
29+
30+
EnumWindows(callback.nativeFunction, 0);
31+
callback.close();
32+
return found;
33+
}
34+
35+
/// Mark the current window with the instance hash
36+
static void markCurrentWindow(String instanceId) {
37+
final int idValue = instanceId.isEmpty ? "default".hashCode : instanceId.hashCode;
38+
39+
// Find the window belonging to the current process
40+
final callback = NativeCallable<WNDENUMPROC>.isolateLocal((int hwnd, int lParam) {
41+
final lpdwProcessId = calloc<Uint32>();
42+
GetWindowThreadProcessId(hwnd, lpdwProcessId);
43+
44+
// If this window belongs to our PID and is a top-level visible window
45+
if (lpdwProcessId.value == pid && IsWindowVisible(hwnd) != 0) {
46+
final propPtr = _propName.toNativeUtf16();
47+
SetProp(hwnd, propPtr, idValue);
48+
free(propPtr);
49+
free(lpdwProcessId);
50+
return 0; // Found our window, stop
51+
}
52+
53+
free(lpdwProcessId);
54+
return 1;
55+
}, exceptionalReturn: 0);
56+
57+
EnumWindows(callback.nativeFunction, 0);
58+
callback.close();
59+
}
60+
61+
static bool wakeUpByProp(String instanceId) {
62+
final int targetId = instanceId.isEmpty ? "default".hashCode : instanceId.hashCode;
63+
return _findAndWake(targetId);
64+
}
65+
}

0 commit comments

Comments
 (0)