fix(client): 自动连接死锁兜底(轮询)+ 逐次门控日志(1.0.27)
复现:armed→设置已加载后无 ready。死锁:selLoaded 靠 selectedNode controller 的 loaded 标志, 而该标志完成时若选中值未变(==默认)不会发通知,若 nodeSub 已先于它触发则此后无人再调 tryGo。 修:_maybeAutoConnect 加 Timer.periodic(400ms,≤12s)兜底复查,decided 后停;并恢复每次 tryGo 打 check 日志(settingsLoaded/selLoaded/node uuid)以确认卡点。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+20
-3
@@ -1,5 +1,6 @@
|
||||
// main.dart — 入口(演示态)
|
||||
// 串接:登录 → 引导 → 主框架(4 Tab);状态层统一走 Riverpod。
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
@@ -133,7 +134,14 @@ class _RootFlowState extends ConsumerState<RootFlow> {
|
||||
ProviderSubscription<AsyncValue<List<Node>>>? nodeSub;
|
||||
ProviderSubscription<AppSettings>? settingsSub;
|
||||
ProviderSubscription<String>? selSub;
|
||||
Timer? poll;
|
||||
var decided = false;
|
||||
void finish() {
|
||||
nodeSub?.close();
|
||||
settingsSub?.close();
|
||||
selSub?.close();
|
||||
poll?.cancel();
|
||||
}
|
||||
void tryGo() {
|
||||
if (decided) return;
|
||||
final settings = ref.read(settingsProvider);
|
||||
@@ -142,11 +150,11 @@ class _RootFlowState extends ConsumerState<RootFlow> {
|
||||
// 否则可能在 effectiveNode 尚是占位/未就绪时就触发,_connect 报「节点尚未就绪」。
|
||||
// 还要等设置 + 选中节点都从持久化恢复完,否则可能用到未恢复的默认(智能)而非上次选择。
|
||||
final node = ref.read(effectiveNodeProvider);
|
||||
logLine('AutoConnect',
|
||||
'check: settingsLoaded=${settings.loaded} selLoaded=$selLoaded node=${node.code} uuid=${node.uuid.isEmpty ? "EMPTY" : "ok"}');
|
||||
if (!settings.loaded || !selLoaded || node.uuid.isEmpty) return;
|
||||
decided = true;
|
||||
nodeSub?.close();
|
||||
settingsSub?.close();
|
||||
selSub?.close();
|
||||
finish();
|
||||
final phase = ref.read(connectionProvider).phase;
|
||||
logLine('AutoConnect', 'ready: autoConnect=${settings.autoConnect} phase=$phase node=${node.code} uuid=${node.uuid.isEmpty ? "EMPTY" : "ok"}');
|
||||
if (settings.autoConnect && phase == VpnPhase.off) {
|
||||
@@ -163,6 +171,15 @@ class _RootFlowState extends ConsumerState<RootFlow> {
|
||||
nodeSub = ref.listenManual(nodesProvider, (_, __) => tryGo());
|
||||
settingsSub = ref.listenManual(settingsProvider, (_, __) => tryGo());
|
||||
selSub = ref.listenManual(selectedNodeCodeProvider, (_, __) => tryGo());
|
||||
// 兜底轮询:有些就绪信号(如选中节点 _load 完成但值未变,不会发通知)不会触发上面的监听,
|
||||
// 靠定时复查避免「明明都就绪了却没人再调 tryGo」的死锁。最多等 ~12s。
|
||||
poll = Timer.periodic(const Duration(milliseconds: 400), (t) {
|
||||
if (decided || t.tick > 30) {
|
||||
t.cancel();
|
||||
return;
|
||||
}
|
||||
tryGo();
|
||||
});
|
||||
tryGo();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
name: pangolin_vpn
|
||||
description: 穿山甲 · Pangolin — 极简、稳定、跨平台网络加速客户端。
|
||||
publish_to: "none"
|
||||
version: 1.0.26+27
|
||||
version: 1.0.27+28
|
||||
|
||||
environment:
|
||||
sdk: ^3.5.0
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
; 前置: 先在 client/ 跑 `flutter build windows`(Release),产物在
|
||||
; ..\..\build\windows\x64\runner\Release
|
||||
#define MyAppName "穿山甲 Pangolin"
|
||||
#define MyAppVersion "1.0.26"
|
||||
#define MyAppVersion "1.0.27"
|
||||
#define MyAppPublisher "Pangolin"
|
||||
#define MyAppExeName "pangolin_vpn.exe"
|
||||
#define BuildDir "..\..\build\windows\x64\runner\Release"
|
||||
|
||||
Reference in New Issue
Block a user