atmob_location_client.dart 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. import 'dart:async';
  2. import 'dart:convert';
  3. import 'package:flutter_map/src/entity/map_location.dart';
  4. import 'package:injectable/injectable.dart';
  5. import 'package:location/data/repositories/friends_repository.dart';
  6. import 'package:location/di/get_it.dart';
  7. import 'package:location/helper/internet_connection_helper.dart';
  8. import 'package:location/socket/base_message.dart';
  9. import 'package:location/socket/socket_constants.dart';
  10. import 'package:location/utils/async_util.dart';
  11. import 'package:location/utils/atmob_log.dart';
  12. import 'package:location/utils/base_expand.dart';
  13. import 'package:web_socket_channel/web_socket_channel.dart';
  14. import '../data/bean/location_info.dart';
  15. import '../data/repositories/account_repository.dart';
  16. import '../data/repositories/contact_repository.dart';
  17. import '../data/repositories/message_repository.dart';
  18. import '../dialog/account_replace_dialog.dart';
  19. import 'location_message.dart';
  20. typedef OnLocationChangeListener = void Function(List<LocationInfo> data);
  21. @lazySingleton
  22. class AtmobLocationClient {
  23. static final String tag = 'AtmobLocationClient';
  24. static final List<OnLocationChangeListener> _locationListeners = [];
  25. WebSocketChannel? _webSocket;
  26. StreamSubscription? _subscription;
  27. bool _isConnecting = false;
  28. CancelableFuture? cancelableFuture;
  29. final InternetConnectionHelper internetConnectionHelper;
  30. AtmobLocationClient(this.internetConnectionHelper) {
  31. startWebsocketInternal();
  32. }
  33. static AtmobLocationClient getAtmobLocationClient() {
  34. return getIt.get<AtmobLocationClient>();
  35. }
  36. static void connectWebSocket() {
  37. AtmobLog.d(tag, 'connectWebSocket');
  38. AtmobLocationClient client = getIt.get<AtmobLocationClient>();
  39. client.startWebsocketInternal();
  40. }
  41. static void disConnectWebSocket() {
  42. AtmobLog.d(tag, 'disConnectWebSocket');
  43. AtmobLocationClient client = getIt.get<AtmobLocationClient>();
  44. client.stopWebsocketInternal();
  45. }
  46. void startWebsocketInternal() {
  47. if (AccountRepository.token == null ||
  48. AccountRepository.token?.isEmpty == true ||
  49. _isConnecting) {
  50. return;
  51. }
  52. cancelableFuture?.cancel();
  53. cancelableFuture = AsyncUtil.retryWithExponentialBackoff(
  54. () => _startConnect(), 4,
  55. initialInterval: Duration(seconds: 2));
  56. cancelableFuture!.catchError((error) {
  57. AtmobLog.d(tag, '重试最大次数 异常 error:$error');
  58. startWebsocketInternal();
  59. });
  60. }
  61. void stopWebsocketInternal() {
  62. AtmobLog.d(tag, 'stopWebsocketInternal');
  63. cancelableFuture?.cancel();
  64. _disposePreviousConnection();
  65. }
  66. void _disposePreviousConnection() {
  67. _webSocket?.sink.close();
  68. _webSocket = null;
  69. _subscription?.cancel();
  70. _subscription = null;
  71. _isConnecting = false;
  72. }
  73. Future<void> _startConnect() async {
  74. AtmobLog.d(tag, '_startConnect');
  75. _disposePreviousConnection();
  76. final webSocket = WebSocketChannel.connect(Uri.parse(
  77. '${SocketConstants.locationBaseUrl}${AccountRepository.token}'));
  78. _webSocket = webSocket;
  79. try {
  80. await webSocket.ready;
  81. AtmobLog.d(tag, 'webSocket 连接成功');
  82. _isConnecting = true;
  83. } catch (e) {
  84. AtmobLog.d(tag, 'webSocket 连接失败 error:$e');
  85. rethrow;
  86. }
  87. _subscription = webSocket.stream
  88. .map((s) {
  89. AtmobLog.d(tag, 'webSocket receive:$s');
  90. try {
  91. Map<String, dynamic> data = jsonDecode(s);
  92. return BaseMessage.fromJson(data);
  93. } catch (e) {
  94. AtmobLog.d(tag, 'BaseMessage jsonDecode error:$e');
  95. }
  96. return null;
  97. })
  98. .where((message) {
  99. if (message == null || message.cmd == null) {
  100. return false;
  101. }
  102. switch (message.cmd) {
  103. case SocketConstants.refreshFriendList:
  104. FriendsRepository.getInstance().refreshFriends();
  105. break;
  106. case SocketConstants.refreshFriendRequest:
  107. MessageRepository.getInstance().refreshFriendWaitingCount();
  108. break;
  109. case SocketConstants.refreshFriendMessage:
  110. MessageRepository.getInstance().refreshUnreadMessage();
  111. break;
  112. case SocketConstants.refreshContact:
  113. ContactRepository.getInstance().refreshContactList();
  114. break;
  115. case SocketConstants.refreshMember:
  116. AccountRepository.getInstance().refreshMemberStatus();
  117. break;
  118. case SocketConstants.refreshUserLogin:
  119. AccountReplaceDialog.show();
  120. AccountRepository.getInstance().logout();
  121. break;
  122. }
  123. return SocketConstants.receiveFriendBatchLocation == message.cmd;
  124. })
  125. .map((message) => message?.data)
  126. .where((data) => data != null)
  127. .cast<String>()
  128. .map((s) {
  129. try {
  130. List<dynamic> jsonList = jsonDecode(s);
  131. return jsonList.map((e) => LocationInfo.fromJson(e)).toList();
  132. } catch (e) {
  133. AtmobLog.d(tag, 'List<LocationInfo> jsonDecode error:$e');
  134. }
  135. })
  136. .bufferTime(Duration(seconds: 5))
  137. .where((locationInfos) => locationInfos.isNotEmpty)
  138. .map((lists) {
  139. Map<String, LocationInfo> idLocation = {};
  140. for (var list in lists) {
  141. if (list == null || list.isEmpty) {
  142. continue;
  143. }
  144. for (var location in list) {
  145. String? userId = location.userId;
  146. if (userId == null ||
  147. location.longitude == 0 ||
  148. location.latitude == 0) {
  149. continue;
  150. }
  151. idLocation[userId] = location;
  152. }
  153. }
  154. return idLocation.values.toList();
  155. })
  156. .listen(
  157. _handleLocationMessage,
  158. onError: _handleError,
  159. onDone: _handleDisconnect,
  160. );
  161. }
  162. void _handleLocationMessage(List<LocationInfo> data) {
  163. AtmobLog.d(tag, '接收到位置信息: ${data.length}');
  164. for (var listener in _locationListeners) {
  165. listener(data);
  166. }
  167. }
  168. // 断开处理
  169. void _handleDisconnect() {
  170. AtmobLog.e(tag, 'WebSocket 断开连接');
  171. _isConnecting = false;
  172. startWebsocketInternal();
  173. }
  174. void _handleError(error) {
  175. AtmobLog.e(tag, 'WebSocket 错误: $error');
  176. }
  177. static void addLocationListener(OnLocationChangeListener listener) {
  178. _locationListeners.add(listener);
  179. }
  180. static void removeLocationListener(OnLocationChangeListener listener) {
  181. _locationListeners.remove(listener);
  182. }
  183. void _sendMessage(String msg) {
  184. if (_webSocket == null) {
  185. return;
  186. }
  187. _webSocket!.sink.add(msg);
  188. AtmobLog.d(tag, 'send location: $msg');
  189. }
  190. void uploadLocation(MapLocation location) async {
  191. if (_webSocket == null ||
  192. location.latitude == 0 && location.longitude == 0 ||
  193. location.address == null ||
  194. location.address!.isEmpty) {
  195. return;
  196. }
  197. String? network;
  198. try {
  199. network = await internetConnectionHelper
  200. .getNetworkName()
  201. .timeout(Duration(seconds: 2));
  202. } catch (e) {}
  203. _sendMessage(LocationMessage.obtainMessage(location, network));
  204. }
  205. }