Skip to content

[BUG] 反初始化之后,重新初始化,更换了IP,SDK监听器 onConnectFailed报错,错误码:10000,返回的错误信息还是 错误信息: dial tcp xxx.xxx.xxx.xxx:10001: i/o timeout  #174

@k178613133

Description

@k178613133

OpenIM Server Version

3.8.3+3

Operating System and CPU Architecture

macOS (AMD)

Deployment Method

第二次的初始化SDK,监听器 onConnectFailed报错,错误码:10000,返回的错误信息还是 错误信息: dial tcp xxx.xxx.xxx.xxx:10001: i/o timeout

第二次初始化SDK的监听器报错,提示的IP,是首次初始化SDK填写的IP,已经调用了反初始化,然后重新初始化更换了IP了,为啥第二次SDK,还是报错的首次初始化SDK的IP。

然后第二次初始化 监听器 onConnectFailed会一直报错,说连接不上第一次初始化SDK的ip, 不过关闭了app之后,重新打开。第二次初始化就成功了。这里就很奇怪。

是反初始化的时候,没有把首次初始化SDK里面的数据清理完整吗?

Bug Description and Steps to Reproduce

import 'dart:convert';
import 'dart:io';
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:openim_live/openim_live.dart';
import 'package:dio/dio.dart' as dio;
import '../config/domain_config.dart';
import '../../utils/AESUtils.dart' as LocalAESUtils;

import '../im_callback.dart';

class IMController extends GetxController with IMCallback, OpenIMLive {
late Rx userInfo;
late String atAllTag;

// 重连相关变量
int _reconnectAttempts = 0;
static const int _maxReconnectAttempts = 0; // 最大重连次数
static const Duration _reconnectDelay = Duration(seconds: 0);
bool _isReconnecting = false;

@OverRide
void onClose() {
super.close();
onCloseLive();
super.onClose();
}

@OverRide
void onInit() async {
super.onInit();
onInitLive();
WidgetsBinding.instance.addPostFrameCallback((_) => initOpenIM());
}

void initOpenIM() async {
// 打印初始化参数
print('=== SDK初始化参数 ===');
print('platformID: ${IMUtils.getPlatform()}');
print('apiAddr: ${Config.imApiUrl}');
print('wsAddr: ${Config.imWsUrl}');
print('dataDir: ${Config.cachePath}');
print('logLevel: ${Config.logLevel}');
print('logFilePath: ${Config.cachePath}');
print('====================');

final initialized = await OpenIM.iMManager.initSDK(
  platformID: IMUtils.getPlatform(),
  apiAddr: Config.imApiUrl,
  wsAddr: Config.imWsUrl,
  dataDir: Config.cachePath,
  logLevel: Config.logLevel,
  logFilePath: Config.cachePath,
  listener: OnConnectListener(
    onConnecting: () {
      print("重新连接-init");
      imSdkStatus(IMSdkStatus.connecting);
    },
    onConnectFailed: (code, error) {
      print("连接失败-init");
      imSdkStatus(IMSdkStatus.connectionFailed);
      _handleConnectionFailedWithRetry(code ?? 0, error ?? 'Unknown error');
    },
    onConnectSuccess: () {
      imSdkStatus(IMSdkStatus.connectionSucceeded);
      _resetReconnectState();
    },
    onKickedOffline: kickedOffline,
    onUserTokenExpired: kickedOffline,
    onUserTokenInvalid: userTokenInvalid,
  ),
);

OpenIM.iMManager
  ..setUploadLogsListener(OnUploadLogsListener(onUploadProgress: uploadLogsProgress))
  ..userManager.setUserListener(OnUserListener(
      onSelfInfoUpdated: (u) {
        selfInfoUpdated(u);

        userInfo.update((val) {
          val?.nickname = u.nickname;
          val?.faceURL = u.faceURL;

          val?.remark = u.remark;
          val?.ex = u.ex;
          val?.globalRecvMsgOpt = u.globalRecvMsgOpt;
        });
      },
      onUserStatusChanged: userStausChanged))
  ..messageManager.setAdvancedMsgListener(OnAdvancedMsgListener(
    onRecvC2CReadReceipt: recvC2CMessageReadReceipt,
    onRecvNewMessage: recvNewMessage,
    onNewRecvMessageRevoked: recvMessageRevoked,
    onRecvOfflineNewMessage: recvOfflineMessage,
    onRecvOnlineOnlyMessage: (msg) {
      print('onRecvOnlineOnlyMessage'); 
      if (msg.isCustomType) {
      
        final data = msg.customElem!.data;
        final map = jsonDecode(data!);
        final customType = map['customType'];
        if (customType == CustomMessageType.callingInvite ||
            customType == CustomMessageType.callingAccept ||
            customType == CustomMessageType.callingReject ||
            customType == CustomMessageType.callingCancel ||
            customType == CustomMessageType.callingHungup) {
          final signaling = SignalingInfo(invitation: InvitationInfo.fromJson(map['data']));
          signaling.userID = signaling.invitation?.inviterUserID;

          switch (customType) {
            case CustomMessageType.callingInvite:
              receiveNewInvitation(signaling);
              break;
            case CustomMessageType.callingAccept:
              inviteeAccepted(signaling);
              break;
            case CustomMessageType.callingReject:
              inviteeRejected(signaling);
              break;
            case CustomMessageType.callingCancel:
              invitationCancelled(signaling);
              break;
            case CustomMessageType.callingHungup:
              beHangup(signaling);
              break;
          }
        }
      }
    },
  ))
  ..messageManager.setMsgSendProgressListener(OnMsgSendProgressListener(
    onProgress: progressCallback,
  ))
  ..messageManager.setCustomBusinessListener(OnCustomBusinessListener(
    onRecvCustomBusinessMessage: recvCustomBusinessMessage,
  ))
  ..friendshipManager.setFriendshipListener(OnFriendshipListener(
    onBlackAdded: blacklistAdded,
    onBlackDeleted: blacklistDeleted,
    onFriendApplicationAccepted: friendApplicationAccepted,
    onFriendApplicationAdded: friendApplicationAdded,
    onFriendApplicationDeleted: friendApplicationDeleted,
    onFriendApplicationRejected: friendApplicationRejected,
    onFriendInfoChanged: friendInfoChanged,
    onFriendAdded: friendAdded,
    onFriendDeleted: friendDeleted,
  ))
  ..conversationManager.setConversationListener(OnConversationListener(
      onConversationChanged: conversationChanged,
      onNewConversation: newConversation,
      onTotalUnreadMessageCountChanged: totalUnreadMsgCountChanged,
      onInputStatusChanged: inputStateChanged,
      onSyncServerFailed: (reInstall) {
        imSdkStatus(IMSdkStatus.syncFailed, reInstall: reInstall ?? false);
        _handleSyncFailedWithRetry(reInstall);
      },
      onSyncServerFinish: (reInstall) {
        imSdkStatus(IMSdkStatus.syncEnded, reInstall: reInstall ?? false);
        _resetReconnectState();
        if (Platform.isAndroid) {
          Permissions.request([Permission.systemAlertWindow]);
        }
      },
      onSyncServerStart: (reInstall) {
        imSdkStatus(IMSdkStatus.syncStart, reInstall: reInstall ?? false);
      },
      onSyncServerProgress: (progress) {
        imSdkStatus(IMSdkStatus.syncProgress, progress: progress);
      }))
  ..groupManager.setGroupListener(OnGroupListener(
    onGroupApplicationAccepted: groupApplicationAccepted,
    onGroupApplicationAdded: groupApplicationAdded,
    onGroupApplicationDeleted: groupApplicationDeleted,
    onGroupApplicationRejected: groupApplicationRejected,
    onGroupInfoChanged: groupInfoChanged,
    onGroupMemberAdded: groupMemberAdded,
    onGroupMemberDeleted: groupMemberDeleted,
    onGroupMemberInfoChanged: groupMemberInfoChanged,
    onJoinedGroupAdded: joinedGroupAdded,
    onJoinedGroupDeleted: joinedGroupDeleted,
  ));

Logger().sdkIsInited = initialized;
initializedSubject.sink.add(initialized);

}

Future login(String userID, String token) async {
try {
var user = await OpenIM.iMManager.login(
userID: userID,
token: token,
defaultValue: () async => UserInfo(userID: userID),
);
userInfo = UserFullInfo.fromJson(user.toJson()).obs;
_queryMyFullInfo();
_queryAtAllTag();
} catch (e, s) {
print('e: $e s:$s');
await _handleLoginRepeatError(e);

  return Future.error(e, s);
}

}

Future logout() {
return OpenIM.iMManager.logout();
}

void _queryAtAllTag() async {
atAllTag = OpenIM.iMManager.conversationManager.atAllTag;
}

void _queryMyFullInfo() async {
final data = await Apis.queryMyFullInfo();
if (data is UserFullInfo) {
userInfo.update((val) {
val?.allowAddFriend = data.allowAddFriend;
val?.allowBeep = data.allowBeep;
val?.allowVibration = data.allowVibration;
val?.nickname = data.nickname;
val?.faceURL = data.faceURL;
val?.phoneNumber = data.phoneNumber;
val?.email = data.email;
val?.birth = data.birth;
val?.gender = data.gender;
});
}
}

_handleLoginRepeatError(e) async {
if (e is PlatformException && (e.code == "13002" || e.code == '1507')) {
await logout();
await DataSp.removeLoginCertificate();
}
}

/// 处理连接失败并尝试重连
void _handleConnectionFailedWithRetry(int code, String error) async {
if (_isReconnecting) {
print('正在重连中,忽略新的连接失败事件');
return;
}

_reconnectAttempts++;
print('连接失败,第 $_reconnectAttempts 次重连尝试,错误码: $code, 错误信息: $error');

if (_reconnectAttempts <= _maxReconnectAttempts) {
  // 尝试重连
  await _attemptReconnect();
} else {
  // 重连次数已达上限,反初始化SDK并重新初始化
  print('重连次数已达上限 ($_maxReconnectAttempts 次),开始反初始化SDK并重新初始化');
  await _restartSDKWithLatestConfig();
}

}

/// 处理同步失败并尝试重连
void _handleSyncFailedWithRetry(bool? reInstall) async {
if (_isReconnecting) {
print('正在重连中,忽略新的同步失败事件');
return;
}

_reconnectAttempts++;
print('同步失败,第 $_reconnectAttempts 次重连尝试');

if (_reconnectAttempts <= _maxReconnectAttempts) {
  // 尝试重连
  await _attemptReconnect();
} else {
  // 重连次数已达上限,反初始化SDK并重新初始化
  print('重连次数已达上限 ($_maxReconnectAttempts 次),开始反初始化SDK并重新初始化');
  await _restartSDKWithLatestConfig();
}

}

/// 尝试重连
Future _attemptReconnect() async {
if (_isReconnecting) {
return;
}

_isReconnecting = true;
print('等待 $_reconnectDelay.inSeconds 秒后进行第 $_reconnectAttempts 次重连...');

await Future.delayed(_reconnectDelay);

print('开始第 $_reconnectAttempts 次重连...');

try {
  // 由于不需要重新初始化SDK,我们只需要等待一段时间
  // 让SDK有机会自动重连,或者等待网络状态变化
  
  // 等待一段时间让重连有机会发生
  await Future.delayed(Duration(seconds: 5));
  
  // 检查当前连接状态
  // 这里我们可以通过检查SDK状态来判断是否重连成功
  // 或者等待下一次连接状态回调
  
  print('第 $_reconnectAttempts 次重连等待完成,检查连接状态...');
  
  // 重连尝试完成,重置状态
  // 如果连接成功,会在onConnectSuccess回调中重置状态
  // 如果连接失败,会在onConnectFailed回调中继续重连
  _isReconnecting = false;
  
} catch (e) {
  print('第 $_reconnectAttempts 次重连失败: $e');
  _isReconnecting = false;
}

}

/// 重置重连状态
void _resetReconnectState() {
_reconnectAttempts = 0;
_isReconnecting = false;
print('重连状态已重置');
}

/// 获取重连状态信息
Map<String, dynamic> getReconnectStatus() {
return {
'isReconnecting': _isReconnecting,
'reconnectAttempts': _reconnectAttempts,
'maxReconnectAttempts': _maxReconnectAttempts,
'reconnectDelay': _reconnectDelay.inSeconds,
};
}

/// 手动重置重连状态
void resetReconnectState() {
_resetReconnectState();
}

/// 反初始化SDK并重新初始化
Future _restartSDKWithLatestConfig() async {
try {
print('开始反初始化SDK...');

  // // 反初始化SDK
  // OpenIM.iMManager.unInitSDK();
  // print('SDK反初始化成功');
  
  // 重置重连状态
  _resetReconnectState();
  
  // 根据最新的appid重新请求服务器信息
  print('开始获取最新的服务器配置...');
  await _refreshServerConfig();
  
  // 重新初始化SDK
  print('开始重新初始化SDK...');
  await _reinitializeSDK();
  
  // 等待连接成功回调后再进行登录
  print('等待连接成功回调...');
  await _waitForConnectionSuccess();
  
  // 重新登录
  print('开始重新登录...');
  await _relogin();
  
  print('SDK重启和重新初始化完成');
  
} catch (e) {
  print('SDK重启失败: $e');
  // 如果重启失败,不再显示弹窗,而是记录错误日志
  // 系统会继续尝试重连,直到达到最大重连次数
  print('SDK重启失败,系统将继续尝试重连');
}

}

/// 刷新服务器配置
Future _refreshServerConfig() async {
try {
// 获取最新的appid
final latestAppId = await _getLatestAppId();
print('获取到最新appid: $latestAppId');

  // 根据appid重新请求服务器信息
  await _requestServerConfig(latestAppId);
  print('服务器配置刷新成功');
  
} catch (e) {
  print('刷新服务器配置失败: $e');
  throw e;
}

}

/// 获取最新的appid
Future _getLatestAppId() async {
try {
// 从本地存储获取最新appid
final appId = await DataSp.getCurrentAppId();
if (appId != null && appId.isNotEmpty) {
print('获取到最新appid: $appId');
return appId;
}

  // 如果没有找到appid,尝试从历史记录中获取
  final appIdHistory = await DataSp.getAppIdHistory();
  if (appIdHistory != null && appIdHistory.isNotEmpty) {
    final latestAppId = appIdHistory.first;
    print('从历史记录获取到appid: $latestAppId');
    return latestAppId;
  }
  
  // 如果都没有找到,抛出异常
  throw Exception('没有找到可用的appid');
  
} catch (e) {
  print('获取最新appid失败: $e');
  throw e;
}

}

/// 请求服务器配置
Future _requestServerConfig(String appId) async {
try {
print('使用appid $appId 请求服务器配置');

  // 构造要加密的数据
  final Map<String, dynamic> dataToEncrypt = {
    'appid': appId,
    // 可按需扩展更多字段
  };
  final String jsonString = jsonEncode(dataToEncrypt);
  final String encryptedData = LocalAESUtils.AESUtils.encrypt(jsonString);

  // 使用统一的域名配置
  final List<String> domains = DomainConfig.domains;

  final httpClient = dio.Dio();
  // 设置超时时间
  httpClient.options.connectTimeout = const Duration(seconds: 10);
  httpClient.options.receiveTimeout = const Duration(seconds: 10);
  
  dio.Response<dynamic>? response;

  // 依次尝试每个域名
  for (int i = 0; i < domains.length; i++) {
    final domain = domains[i];
    try {
      print('尝试域名 ${i + 1}/${domains.length}: $domain');
      
      response = await httpClient.post(
        '$domain/api/imserver/getbyappid',
        data: {
          'updateInfo': encryptedData,
        },
      );

      if (response.statusCode == 200) {
        print('域名 $domain 请求成功');
        break;
      } else {
        print('域名 $domain 返回错误状态码: ${response.statusCode}');
      }
    } catch (e) {
      print('域名 $domain 请求失败: $e');
      // 继续尝试下一个域名
      continue;
    }
  }

  if (response != null && response.statusCode == 200) {
    // 解析数据并写入配置
    final Map<String, dynamic> configData = response.data is String
      ? jsonDecode(response.data)
      : response.data;
    print('获取到服务器配置: ${configData['data']['baseUrl']}');
    
    // 组装要存储的配置
    final Map<String, String> serverConfig = {
      'baseUrl': configData['data']['baseUrl'] ?? '',
      'userAgreementUrl': configData['data']['userAgreementUrl'] ?? '',
      'privacyPolicyUrl': configData['data']['privacyPolicyUrl'] ?? '',
      'serverIP': configData['data']['host'] ?? '',
      // 可以根据需要添加更多配置项
    };

    // 保存服务器配置到本地存储
    await DataSp.putServerConfig(serverConfig);
    
    // 保存当前使用的appid
    await DataSp.putCurrentAppId(appId);
    
    print('服务器配置更新成功');
    
    // 更新Config中的连接信息(如果Config支持动态更新)
    await _updateConnectionConfig(serverConfig);
    
  } else {
    // 所有域名都失败了
    print('所有域名都请求失败');
    throw Exception('所有服务器都无法访问,请检查网络连接');
  }
  
} catch (e) {
  print('请求服务器配置失败: $e');
  throw e;
}

}

/// 更新连接配置
Future _updateConnectionConfig(Map<String, String> serverConfig) async {
try {
// 这里可以根据获取到的服务器配置更新Config中的连接信息
// 例如:更新API地址、WebSocket地址等

  // 示例:如果Config支持动态更新
  // Config.updateApiUrl(serverConfig['baseUrl']);
  // Config.updateWsUrl(serverConfig['wsUrl']);
  
  print('连接配置更新完成');
  
} catch (e) {
  print('更新连接配置失败: $e');
  // 不抛出异常,因为主要配置已经保存成功
}

}

/// 重新初始化SDK
Future _reinitializeSDK() async {
try {
// 反初始化SDK
OpenIM.iMManager.unInitSDK();
print('SDK反初始化成功');

  // 打印重新初始化参数
  print('=== SDK重新初始化参数 ===');
  print('platformID2222: ${IMUtils.getPlatform()}');
  print('apiAddr11111: ${Config.imApiUrl}');
  print('wsAddr3333: ${Config.imWsUrl}');
  print('dataDir444: ${Config.cachePath}');
  print('logLevel5555: ${Config.logLevel}');
  print('logFilePath66666: ${Config.cachePath}');
  print('====================');
  
  // 重新初始化SDK,设置新的连接信息
  final initialized = await OpenIM.iMManager.initSDK(
    platformID: IMUtils.getPlatform(),
    apiAddr: Config.imApiUrl,
    wsAddr: Config.imWsUrl,
    dataDir: Config.cachePath,
    logLevel: Config.logLevel,
    logFilePath: Config.cachePath,
    listener: OnConnectListener(
      onConnecting: () {
        print("重新连接-re");
        imSdkStatus(IMSdkStatus.connecting);
      },
      onConnectFailed: (code, error) {
      print("连接失败-re");
        imSdkStatus(IMSdkStatus.connectionFailed);
        // 重新初始化后,如果连接失败,仍然遵循2次重连限制
        _handleConnectionFailedWithRetry(code ?? 0, error ?? 'Unknown error');
      },
      onConnectSuccess: () {
        imSdkStatus(IMSdkStatus.connectionSucceeded);
        print('重新初始化后连接成功');
        // 通知连接成功,可以继续执行登录
        _notifyConnectionSuccess();
      },
      onKickedOffline: kickedOffline,
      onUserTokenExpired: kickedOffline,
      onUserTokenInvalid: userTokenInvalid,
    ),
  );

  if (!initialized) {
    throw Exception('SDK重新初始化失败');
  }

  // 重新设置各种监听器
  await _setupAllListeners();
  print('SDK重新初始化成功,监听器设置完成');
  
  // 显示服务器信息更新提示对话框
  _showServerUpdateDialog();
  
} catch (e) {
  print('SDK重新初始化失败: $e');
  throw e;
}

}

/// 设置所有监听器
Future _setupAllListeners() async {
try {
// 设置上传日志监听器
OpenIM.iMManager.setUploadLogsListener(OnUploadLogsListener(
onUploadProgress: uploadLogsProgress,
));

  // 设置用户监听器
  OpenIM.iMManager.userManager.setUserListener(OnUserListener(
    onSelfInfoUpdated: (u) {
      selfInfoUpdated(u);
      userInfo.update((val) {
        val?.nickname = u.nickname;
        val?.faceURL = u.faceURL;
        val?.remark = u.remark;
        val?.ex = u.ex;
        val?.globalRecvMsgOpt = u.globalRecvMsgOpt;
      });
    },
    onUserStatusChanged: userStausChanged,
  ));

  // 设置消息监听器
  OpenIM.iMManager.messageManager.setAdvancedMsgListener(OnAdvancedMsgListener(
    onRecvC2CReadReceipt: recvC2CMessageReadReceipt,
    onRecvNewMessage: recvNewMessage,
    onNewRecvMessageRevoked: recvMessageRevoked,
    onRecvOfflineNewMessage: recvOfflineMessage,
    onRecvOnlineOnlyMessage: (msg) {
      print('onRecvOnlineOnlyMessage'); 
      if (msg.isCustomType) {
        final data = msg.customElem!.data;
        final map = jsonDecode(data!);
        final customType = map['customType'];
        if (customType == CustomMessageType.callingInvite ||
            customType == CustomMessageType.callingAccept ||
            customType == CustomMessageType.callingReject ||
            customType == CustomMessageType.callingCancel ||
            customType == CustomMessageType.callingHungup) {
          final signaling = SignalingInfo(invitation: InvitationInfo.fromJson(map['data']));
          signaling.userID = signaling.invitation?.inviterUserID;

          switch (customType) {
            case CustomMessageType.callingInvite:
              receiveNewInvitation(signaling);
              break;
            case CustomMessageType.callingAccept:
              inviteeAccepted(signaling);
              break;
            case CustomMessageType.callingReject:
              inviteeRejected(signaling);
              break;
            case CustomMessageType.callingCancel:
              invitationCancelled(signaling);
              break;
            case CustomMessageType.callingHungup:
              beHangup(signaling);
              break;
          }
        }
      }
    },
  ));

  // 设置消息发送进度监听器
  OpenIM.iMManager.messageManager.setMsgSendProgressListener(OnMsgSendProgressListener(
    onProgress: progressCallback,
  ));

  // 设置自定义业务监听器
  OpenIM.iMManager.messageManager.setCustomBusinessListener(OnCustomBusinessListener(
    onRecvCustomBusinessMessage: recvCustomBusinessMessage,
  ));

  // 设置好友关系监听器
  OpenIM.iMManager.friendshipManager.setFriendshipListener(OnFriendshipListener(
    onBlackAdded: blacklistAdded,
    onBlackDeleted: blacklistDeleted,
    onFriendApplicationAccepted: friendApplicationAccepted,
    onFriendApplicationAdded: friendApplicationAdded,
    onFriendApplicationDeleted: friendApplicationDeleted,
    onFriendApplicationRejected: friendApplicationRejected,
    onFriendInfoChanged: friendInfoChanged,
    onFriendAdded: friendAdded,
    onFriendDeleted: friendDeleted,
  ));

  // 设置会话监听器
  OpenIM.iMManager.conversationManager.setConversationListener(OnConversationListener(
    onConversationChanged: conversationChanged,
    onNewConversation: newConversation,
    onTotalUnreadMessageCountChanged: totalUnreadMsgCountChanged,
    onInputStatusChanged: inputStateChanged,
    onSyncServerFailed: (reInstall) {
      imSdkStatus(IMSdkStatus.syncFailed, reInstall: reInstall ?? false);
      // 重新初始化后,如果同步失败,仍然遵循2次重连限制
      _handleSyncFailedWithRetry(reInstall);
    },
    onSyncServerFinish: (reInstall) {
      imSdkStatus(IMSdkStatus.syncEnded, reInstall: reInstall ?? false);
      print('重新初始化后同步完成');
    },
    onSyncServerStart: (reInstall) {
      imSdkStatus(IMSdkStatus.syncStart, reInstall: reInstall ?? false);
    },
    onSyncServerProgress: (progress) {
      imSdkStatus(IMSdkStatus.syncProgress, progress: progress);
    },
  ));

  // 设置群组监听器
  OpenIM.iMManager.groupManager.setGroupListener(OnGroupListener(
    onGroupApplicationAccepted: groupApplicationAccepted,
    onGroupApplicationAdded: groupApplicationAdded,
    onGroupApplicationDeleted: groupApplicationDeleted,
    onGroupApplicationRejected: groupApplicationRejected,
    onGroupInfoChanged: groupInfoChanged,
    onGroupMemberAdded: groupMemberAdded,
    onGroupMemberDeleted: groupMemberDeleted,
    onGroupMemberInfoChanged: groupMemberInfoChanged,
    onJoinedGroupAdded: joinedGroupAdded,
    onJoinedGroupDeleted: joinedGroupDeleted,
  ));

  print('所有监听器设置完成');
  
} catch (e) {
  print('设置监听器失败: $e');
  throw e;
}

}

/// 重新登录
Future _relogin() async {
try {
// 获取保存的登录凭证
final loginCredentials = await _getSavedLoginCredentials();
if (loginCredentials == null) {
throw Exception('没有找到保存的登录凭证');
}

  print('开始重新登录,用户ID: ${loginCredentials['userID']}');
  
  // 执行登录
  await login(loginCredentials['userID']!, loginCredentials['token']!);
  
  print('重新登录成功');
  
} catch (e) {
  print('重新登录失败: $e');
  throw e;
}

}

/// 获取保存的登录凭证
Future<Map<String, String>?> _getSavedLoginCredentials() async {
try {
// 从本地存储获取保存的登录凭证
final loginCertificate = DataSp.getLoginCertificate();

  if (loginCertificate != null && 
      loginCertificate.userID.isNotEmpty && 
      loginCertificate.imToken.isNotEmpty) {
    
    print('获取到保存的登录凭证: userID=${loginCertificate.userID}');
    
    return {
      'userID': loginCertificate.userID,
      'token': loginCertificate.imToken,
    };
  }
  
  // 如果没有找到登录凭证,尝试从登录账户信息中获取
  final loginAccount = DataSp.getLoginAccount();
  if (loginAccount != null) {
    print('找到登录账户信息,但缺少登录凭证');
    print('登录账户信息: $loginAccount');
  }
  
  print('没有找到有效的登录凭证');
  return null;
  
} catch (e) {
  print('获取保存的登录凭证失败: $e');
  return null;
}

}

// 连接成功等待的Completer
Completer? _connectionSuccessCompleter;

/// 等待连接成功回调
Future _waitForConnectionSuccess() async {
_connectionSuccessCompleter = Completer();

// 设置超时时间,避免无限等待
Timer(const Duration(seconds: 30), () {
  if (!_connectionSuccessCompleter!.isCompleted) {
    _connectionSuccessCompleter!.completeError(
      TimeoutException('等待连接成功超时', const Duration(seconds: 30))
    );
  }
});

try {
  await _connectionSuccessCompleter!.future;
  print('连接成功回调已收到,可以继续执行登录');
} catch (e) {
  print('等待连接成功失败: $e');
  throw e;
}

}

/// 通知连接成功
void _notifyConnectionSuccess() {
if (_connectionSuccessCompleter != null && !_connectionSuccessCompleter!.isCompleted) {
_connectionSuccessCompleter!.complete();
print('已通知连接成功');
}
}

/// 显示服务器信息更新提示对话框
void _showServerUpdateDialog() {
final isAndroid = Platform.isAndroid;

Get.dialog(
  PopScope(
    canPop: false, // 禁止返回键关闭对话框
    child: AlertDialog(
      title: Text(
        StrRes.appSsecurityPrompt,
        style: TextStyle(
          fontSize: 17,
          fontWeight: FontWeight.bold,
        ),
      ),
      content: Text(
        isAndroid 
          ? StrRes.restartAppAndroid
          : StrRes.restartAppiPhone,
        style: TextStyle(fontSize: 16),
      ),
      actions: [
        if (isAndroid) ...[
          TextButton(
            onPressed: () {
              Get.back(); // 关闭对话框
              print('用户确认服务器信息更新,准备关闭应用');
              
              // 延迟一下再关闭应用,确保对话框先关闭
              Future.delayed(Duration(milliseconds: 300), () {
                // 关闭当前应用
                // SystemNavigator.pop();
                   if (Platform.isAndroid) {
                    // Android 上使用 exit(0) 更可靠
                    exit(0);
                  } else if (Platform.isIOS) {
                    // iOS 上使用 SystemNavigator.pop(),因为 exit(0) 可能被拒绝
                    exit(0);
                    SystemNavigator.pop();
                  } else {
                    // 其他平台(比如桌面版)
                    exit(0);
                  }
              });
            },
            child: Text(
              '确认',
              style: TextStyle(
                fontSize: 16,
                color: Colors.blue,
              ),
            ),
          ),
        ],
      ],
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(12),
      ),
    ),
  ),
  barrierDismissible: false, // 禁止点击外部关闭对话框
);

print('显示服务器信息更新提示对话框 - 平台: ${isAndroid ? "Android" : "iOS"}');

}
}

Screenshots Link

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions