199 lines
5.4 KiB
Dart
199 lines
5.4 KiB
Dart
import 'dart:async';
|
|
import 'dart:convert';
|
|
import 'dart:developer';
|
|
import 'dart:io';
|
|
import 'package:flutter/cupertino.dart';
|
|
|
|
typedef MessageCallback = void Function(dynamic message);
|
|
typedef ErrorCallback = void Function(dynamic error);
|
|
typedef DoneCallback = void Function();
|
|
typedef ConnectedCallback = void Function();
|
|
typedef ReconnectCallback = void Function(ReconnectStatus);
|
|
|
|
enum ReconnectStatus { start, trying, success }
|
|
|
|
class WebSocketService with WidgetsBindingObserver {
|
|
// 单例
|
|
static final WebSocketService _instance = WebSocketService._internal();
|
|
static bool doReconnect = false;
|
|
|
|
factory WebSocketService() => _instance;
|
|
|
|
WebSocketService._internal();
|
|
|
|
// 初始化/销毁生命周期
|
|
void init() {
|
|
WidgetsBinding.instance.addObserver(this);
|
|
}
|
|
|
|
void dispose() {
|
|
WidgetsBinding.instance.removeObserver(this);
|
|
_reconnectTimer?.cancel();
|
|
}
|
|
|
|
@override
|
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
|
if (state == AppLifecycleState.resumed) {
|
|
if (doReconnect) {
|
|
// 亮屏,主动检测连接
|
|
_socket?.close();
|
|
_socket = null;
|
|
_startReconnect();
|
|
doReconnect = false;
|
|
}
|
|
} else if (state == AppLifecycleState.paused) {
|
|
doReconnect = true;
|
|
// 锁屏时暂停计时器
|
|
_reconnectTimer?.cancel();
|
|
_reconnectTimer = null;
|
|
}
|
|
}
|
|
|
|
WebSocket? _socket;
|
|
String? _url;
|
|
bool _isConnecting = false;
|
|
bool _manuallyClosed = false;
|
|
Timer? _reconnectTimer;
|
|
|
|
final List<MessageCallback> _messageListeners = [];
|
|
final List<ErrorCallback> _errorListeners = [];
|
|
final List<DoneCallback> _doneListeners = [];
|
|
final List<ConnectedCallback> _connectedListeners = [];
|
|
final List<ReconnectCallback> _reconnectListeners = [];
|
|
|
|
bool _hasTriggeredStart = false;
|
|
|
|
/// 建立连接
|
|
Future<void> connect(String url) async {
|
|
_url = url;
|
|
_manuallyClosed = false;
|
|
_hasTriggeredStart = false;
|
|
_triggerReconnectCallback(ReconnectStatus.start);
|
|
await _tryConnect();
|
|
}
|
|
|
|
/// 发送消息
|
|
void send(String message) {
|
|
if (_socket != null && _socket!.readyState == WebSocket.open) {
|
|
_socket!.add(message);
|
|
} else {
|
|
for (var listener in _errorListeners) {
|
|
listener("WebSocket 未连接,无法发送: $message");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 手动关闭
|
|
Future<void> close() async {
|
|
_manuallyClosed = true;
|
|
_reconnectTimer?.cancel();
|
|
_reconnectTimer = null;
|
|
await _socket?.close();
|
|
_socket = null;
|
|
_hasTriggeredStart = false;
|
|
}
|
|
|
|
/// 添加监听
|
|
void addMessageListener(MessageCallback listener) =>
|
|
_messageListeners.add(listener);
|
|
|
|
void removeMessageListener(MessageCallback listener) =>
|
|
_messageListeners.remove(listener);
|
|
|
|
void addErrorListener(ErrorCallback listener) =>
|
|
_errorListeners.add(listener);
|
|
|
|
void removeErrorListener(ErrorCallback listener) =>
|
|
_errorListeners.remove(listener);
|
|
|
|
void addDoneListener(DoneCallback listener) => _doneListeners.add(listener);
|
|
|
|
void removeDoneListener(DoneCallback listener) =>
|
|
_doneListeners.remove(listener);
|
|
|
|
void addConnectedListener(ConnectedCallback listener) =>
|
|
_connectedListeners.add(listener);
|
|
|
|
void removeConnectedListener(ConnectedCallback listener) =>
|
|
_connectedListeners.remove(listener);
|
|
|
|
void addReconnectListener(ReconnectCallback listener) =>
|
|
_reconnectListeners.add(listener);
|
|
|
|
void removeReconnectListener(ReconnectCallback listener) =>
|
|
_reconnectListeners.remove(listener);
|
|
|
|
/// 内部尝试连接
|
|
Future<void> _tryConnect() async {
|
|
if (_socket != null && _socket!.readyState == WebSocket.open) return;
|
|
if (_isConnecting) return;
|
|
|
|
_isConnecting = true;
|
|
_triggerReconnectCallback(ReconnectStatus.trying);
|
|
|
|
try {
|
|
_socket = await WebSocket.connect(_url!);
|
|
|
|
// 连接成功回调
|
|
for (var callback in _connectedListeners) callback();
|
|
_triggerReconnectCallback(ReconnectStatus.success);
|
|
_hasTriggeredStart = false;
|
|
|
|
_socket!.listen(
|
|
(data) {
|
|
dynamic msg;
|
|
try {
|
|
msg = jsonDecode(data);
|
|
} catch (_) {
|
|
msg = data.toString();
|
|
}
|
|
for (var listener in _messageListeners) listener(msg);
|
|
},
|
|
onError: (error) {
|
|
for (var listener in _errorListeners) listener(error);
|
|
_startReconnect();
|
|
},
|
|
onDone: () {
|
|
for (var listener in _doneListeners) listener();
|
|
_startReconnect();
|
|
},
|
|
cancelOnError: true,
|
|
);
|
|
} catch (e) {
|
|
for (var listener in _errorListeners) listener(e);
|
|
_startReconnect();
|
|
} finally {
|
|
_isConnecting = false;
|
|
}
|
|
}
|
|
|
|
/// 开始重连
|
|
void _startReconnect() {
|
|
if (_manuallyClosed) return;
|
|
if (_reconnectTimer?.isActive ?? false) return;
|
|
|
|
if (!_hasTriggeredStart) {
|
|
_triggerReconnectCallback(ReconnectStatus.start);
|
|
_hasTriggeredStart = true;
|
|
}
|
|
|
|
_reconnectTimer = Timer.periodic(const Duration(seconds: 5), (timer) {
|
|
if (_socket == null || _socket!.readyState != WebSocket.open) {
|
|
_triggerReconnectCallback(ReconnectStatus.trying);
|
|
_tryConnect();
|
|
} else {
|
|
timer.cancel();
|
|
_reconnectTimer = null;
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
/// 触发重连状态回调
|
|
void _triggerReconnectCallback(ReconnectStatus status) {
|
|
for (var listener in _reconnectListeners) {
|
|
listener(status);
|
|
}
|
|
}
|
|
}
|