Files
91fans_android/lib/hj_utils/detect_line_manager.dart
T
2026-09-17 12:34:07 +07:00

98 lines
3.3 KiB
Dart

// ignore_for_file: use_build_context_synchronously
import 'dart:async';
import 'package:dio/dio.dart';
import 'package:jyfans/config/config.dart';
import 'package:jyfans/hj_utils/text_util.dart';
import 'package:jyfans/tools_base/debug_log.dart';
import 'package:jyfans/tools_base/net/http_manager.dart';
//选线管理
class DetectLineManager {
// 在途请求的取消列表,超时时统一取消
final _cancelList = <CancelToken>[];
Future<String> detectLineOnce() async {
// 硬兜底:整个选线流程最多 20s,超时按"无可用线路"处理,避免 UI 永久卡在"选线中..."
String line;
try {
line = await _detectLineOnce().timeout(const Duration(seconds: 20));
} catch (e) {
debugLog("selectLine", "detectLineOnce()...timeout or error:$e");
line = "";
}
_cancelInflight();
_cancelList.clear();
return line;
}
// 取消在途请求;except 为本次胜出的线路,不取消自己
void _cancelInflight({CancelToken? except}) {
for (final t in _cancelList) {
if (t != except) t.cancel();
}
}
/// 一次完整的批量选线过程
Future<String> _detectLineOnce() async {
//线路只来自 Config.lineList:DNS 解析兜底和本地线路缓存都已去掉
return await _pingCheckBatch(Config.lineList);
}
///检查一批线路:并发 ping,谁先成功用谁;全失败返回 ""
Future<String> _pingCheckBatch(List<String> lines) async {
final validLines = lines.where(TextUtil.isNotEmpty).toList();
if (validLines.isEmpty) return "";
final completer = Completer<String>();
final tasks = validLines.map((line) async {
debugLog("selectLine", "_pingCheckBatch()...开始选线:$line");
final cancelToken = CancelToken();
_cancelList.add(cancelToken);
try {
if (await _pingCheck(line, cancelToken)) {
if (!completer.isCompleted) {
debugLog("selectLine", "_pingCheckBatch()...选线成功:$line");
completer.complete(line);
// 竞速:已选到最快线路,立即取消其余在途请求
_cancelInflight(except: cancelToken);
}
} else {
debugLog("selectLine", "_pingCheckBatch()...线路不可用:$line");
}
} catch (e) {
debugLog("selectLine", "_pingCheckBatch()...线路异常:$line $e");
} finally {
_cancelList.remove(cancelToken);
}
}).toList();
// 全部结束仍无成功 → 返回空
Future.wait(tasks).whenComplete(() {
if (!completer.isCompleted) completer.complete("");
});
return completer.future;
}
/// 单次网络请求,业务码 200 才算成功
Future<bool> _pingCheck(String line, [CancelToken? cancelToken]) async {
final startTime = DateTime.now();
final resp = await httpManager.fetchDetectLineResponse(
"$line/api/app/ping/check",
options: Options(
method: "GET",
sendTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 10),
),
cancelToken: cancelToken,
);
debugLog("ping",
"pingCheck()...line:$line cost ${DateTime.now().difference(startTime).inMilliseconds} milSeconds");
if (TextUtil.isNotEmpty(resp.time)) {
httpManager.setServerTime(resp.time);
}
return resp.isSuccess;
}
}