checkNetworkHealth method

Future<NetworkStatus> checkNetworkHealth({
  1. int slowThresholdMs = 3000,
  2. int timeoutMs = 10000,
})

检查网络健康状况 慢阈值: 1500ms 超时时间: 3000ms

返回值:

  • available: 网络可用且速度正常
  • slow: 网络可用但较慢
  • unavailable: 网络不可用

使用示例:

final status = await NetworkUtil.checkNetworkHealth();
switch (status) {
  case NetworkHealthStatus.available:
    // 网络畅通
    break;
  case NetworkHealthStatus.slow:
    // 网络慢,提示用户
    break;
  case NetworkHealthStatus.unavailable:
    // 网络不可用,提示用户
    break;
}
//或者
NetworkUtil.checkNetworkHealth().then((status) {
  if (status == NetworkHealthStatus.unavailable) {
    // 弹窗提示
  }
});

//界面示例:
FutureBuilder<NetworkHealthStatus>(
  future: NetworkUtil.checkNetworkHealth(),
  builder: (context, snapshot) {
    if (!snapshot.hasData) {
      return CircularProgressIndicator();
    }
    switch (snapshot.data) {
      case NetworkHealthStatus.available:
        return Text('网络畅通');
      case NetworkHealthStatus.slow:
        return Text('网络较慢');
      case NetworkHealthStatus.unavailable:
        return Text('网络不可用');
      default:
        return Text('未知状态');
    }
  },
)

Implementation

Future<NetworkStatus> checkNetworkHealth({
  int slowThresholdMs = 3000,
  int timeoutMs = 10000,
}) async {
  final completer = Completer<NetworkStatus>();
  bool hasSuccess = false;

  // 记录每个节点的响应时间
  final Map<String, int> responseTimes = {};

  final urls = _testUrls + extraUrls;

  // 为每个URL创建单独的CancelToken
  final cancelTokens = urls.map((_) => CancelToken()).toList();

  for (var i = 0; i < urls.length; i++) {
    final url = urls[i];
    final cancelToken = cancelTokens[i];

    Logger.log('网络健康检测: 开始请求 $url');
    final stopwatch = Stopwatch()..start();

    Dio()
        .get(
      url,
      options: Options(
        receiveTimeout: Duration(milliseconds: timeoutMs),
        sendTimeout: Duration(milliseconds: timeoutMs),
      ),
      cancelToken: cancelToken,
    )
        .then((response) {
      stopwatch.stop();
      final responseTime = stopwatch.elapsedMilliseconds;
      responseTimes[url] = responseTime;

      Logger.log(
          '网络健康检测: $url 响应 statusCode=${response.statusCode}, 耗时=${responseTime}ms');

      if (response.statusCode == 200) {
        hasSuccess = true;
      }
    }).catchError((e) {
      stopwatch.stop();
      final responseTime = stopwatch.elapsedMilliseconds;
      responseTimes[url] = responseTime;

      Logger.log('网络健康检测: $url 请求失败/超时,耗时=${responseTime}ms,错误: $e');
    });
  }

  // 超时后判断最终状态
  Future.delayed(Duration(milliseconds: timeoutMs + 500), () {
    // 取消所有未完成的请求
    for (var token in cancelTokens) {
      if (!token.isCancelled) {
        token.cancel('检测超时,取消剩余请求');
      }
    }

    // 打印所有节点的响应时间
    Logger.log('网络健康检测: 所有节点响应时间统计 >>>');
    responseTimes.forEach((url, time) {
      Logger.log('节点: $url, 耗时: ${time}ms');
    });
    Logger.log('网络健康检测: 统计结束 <<<');

    if (!completer.isCompleted) {
      if (hasSuccess) {
        // 获取最快的响应时间
        final fastestTime = responseTimes.values.reduce(min);
        fastResponseTime = fastestTime;
        if (fastestTime <= slowThresholdMs) {
          Logger.log('网络健康检测: 最快节点响应时间 ${fastestTime}ms,判定为 available');
          completer.complete(NetworkStatus.available);
        } else {
          Logger.log('网络健康检测: 最快节点响应时间 ${fastestTime}ms,判定为 slow');
          completer.complete(NetworkStatus.slow);
        }
      } else {
        Logger.log('网络健康检测: 所有节点都失败,判定为 unavailable');
        completer.complete(NetworkStatus.unavailable);
      }
    }
  });

  return completer.future;
}