|
|
@@ -1,144 +1,353 @@
|
|
|
package com.narutohuo.xindazhou.common.socketio
|
|
|
|
|
|
import android.app.Application
|
|
|
-import androidx.lifecycle.LifecycleObserver
|
|
|
-import androidx.lifecycle.OnLifecycleEvent
|
|
|
+import androidx.lifecycle.DefaultLifecycleObserver
|
|
|
+import androidx.lifecycle.LifecycleOwner
|
|
|
+import androidx.lifecycle.ProcessLifecycleOwner
|
|
|
import com.narutohuo.xindazhou.common.auth.AuthManager
|
|
|
+import com.narutohuo.xindazhou.common.auth.utils.JWTUtil
|
|
|
import com.narutohuo.xindazhou.common.config.ServerConfigManager
|
|
|
import com.narutohuo.xindazhou.common.log.LogHelper
|
|
|
+import com.narutohuo.xindazhou.common.socketio.impl.SocketIOClient
|
|
|
+import com.narutohuo.xindazhou.common.socketio.model.SocketIOResponse
|
|
|
+import com.narutohuo.xindazhou.core.storage.StorageImpl
|
|
|
import kotlinx.coroutines.CoroutineScope
|
|
|
import kotlinx.coroutines.Dispatchers
|
|
|
import kotlinx.coroutines.SupervisorJob
|
|
|
import kotlinx.coroutines.delay
|
|
|
+import kotlinx.coroutines.flow.MutableSharedFlow
|
|
|
+import kotlinx.coroutines.flow.SharedFlow
|
|
|
+import kotlinx.coroutines.flow.asSharedFlow
|
|
|
import kotlinx.coroutines.launch
|
|
|
|
|
|
/**
|
|
|
- * SocketIO 连接管理器
|
|
|
+ * SocketIO 管理器(业务封装层)
|
|
|
*
|
|
|
- * 负责自动管理 SocketIO 连接的生命周期:
|
|
|
- * - App 进入前台时自动重连
|
|
|
- * - Token 过期时自动刷新并重连
|
|
|
- * - 监听应用生命周期
|
|
|
+ * 与 AuthManager、VersionUpdateManager 类似,提供统一的 Socket.IO 封装
|
|
|
+ * 自动处理连接、重连、Token 刷新等,外部只需要订阅即可
|
|
|
+ *
|
|
|
+ * 职责:
|
|
|
+ * ✅ 自动处理连接(包括 Token 刷新)
|
|
|
+ * ✅ 自动处理重连
|
|
|
+ * ✅ 提供消息订阅接口
|
|
|
+ * ✅ 外部只需要订阅即可
|
|
|
*
|
|
|
* 使用方式:
|
|
|
* ```kotlin
|
|
|
- * // 在 Application.onCreate() 中初始化
|
|
|
- * SocketIOManager.init(application)
|
|
|
+ * // 在 AppInitializer 中初始化(可选,会自动连接)
|
|
|
+ * SocketIOManager.initialize(application)
|
|
|
+ *
|
|
|
+ * // 在 ViewModel 中订阅消息
|
|
|
+ * viewModelScope.launch {
|
|
|
+ * SocketIOManager.shared.subscribe("community_message").collect { response ->
|
|
|
+ * // 处理社区消息
|
|
|
+ * }
|
|
|
+ * }
|
|
|
* ```
|
|
|
*/
|
|
|
-object SocketIOManager : LifecycleObserver {
|
|
|
+object SocketIOManager : DefaultLifecycleObserver {
|
|
|
|
|
|
private const val TAG = "SocketIOManager"
|
|
|
|
|
|
- private var application: Application? = null
|
|
|
+ // 单例实例
|
|
|
+ val shared = SocketIOManager
|
|
|
+
|
|
|
+ private val socketClient = SocketIOClient.getInstance()
|
|
|
private val appScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
|
|
- private var initialized = false
|
|
|
+
|
|
|
+ private var application: Application? = null
|
|
|
+ private var serverURL: String? = null
|
|
|
+ private var isInitialized = false
|
|
|
+
|
|
|
+ // 连接状态(使用 replay = 1 确保新订阅者能立即获取最新状态)
|
|
|
+ private val _connectionState = MutableSharedFlow<Boolean>(extraBufferCapacity = 1, replay = 1)
|
|
|
+ val connectionState: SharedFlow<Boolean> = _connectionState.asSharedFlow()
|
|
|
+
|
|
|
+ init {
|
|
|
+ // 初始化连接状态(同步获取当前状态)
|
|
|
+ _connectionState.tryEmit(isConnected())
|
|
|
+
|
|
|
+ // 订阅连接事件
|
|
|
+ subscribeConnectionEvents()
|
|
|
+ }
|
|
|
+
|
|
|
+ // 消息订阅(使用 SharedFlow)
|
|
|
+ private val eventFlows = mutableMapOf<String, MutableSharedFlow<SocketIOResponse>>()
|
|
|
+
|
|
|
+ // Token 刷新防抖
|
|
|
+ @Volatile
|
|
|
+ private var isRefreshingToken = false
|
|
|
+
|
|
|
+ @Volatile
|
|
|
+ private var lastTokenRefreshTime = 0L
|
|
|
+
|
|
|
+ // 重连防抖
|
|
|
+ @Volatile
|
|
|
+ private var isReconnecting = false
|
|
|
+
|
|
|
+ @Volatile
|
|
|
+ private var lastReconnectTime = 0L
|
|
|
|
|
|
/**
|
|
|
- * 初始化 SocketIO 管理器
|
|
|
+ * 初始化并自动连接(如果用户已登录)
|
|
|
*
|
|
|
- * 需要在 Application.onCreate() 中调用
|
|
|
- * 会自动注册生命周期监听
|
|
|
+ * 在 AppInitializer 中调用一次即可
|
|
|
+ * 后续会自动处理连接、重连、Token 刷新等
|
|
|
*
|
|
|
- * @param app Application 实例
|
|
|
+ * 注意:
|
|
|
+ * - 如果用户未登录,不会立即连接,等待登录成功后调用 ensureConnected()
|
|
|
+ * - 如果用户已登录,会自动连接
|
|
|
+ *
|
|
|
+ * @param application Application 实例(用于生命周期监听)
|
|
|
*/
|
|
|
- fun init(app: Application) {
|
|
|
- if (initialized) {
|
|
|
- LogHelper.w(TAG, "SocketIOManager 已初始化,跳过重复初始化")
|
|
|
+ fun initialize(application: Application) {
|
|
|
+ if (isInitialized) {
|
|
|
+ LogHelper.d(TAG, "已初始化,跳过")
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- application = app
|
|
|
- initialized = true
|
|
|
+ this.application = application
|
|
|
+ isInitialized = true
|
|
|
|
|
|
- // 注册 App 生命周期监听(使用反射,避免强依赖)
|
|
|
+ // 注册 App 生命周期监听
|
|
|
try {
|
|
|
- val processLifecycleOwnerClass = Class.forName("androidx.lifecycle.ProcessLifecycleOwner")
|
|
|
- val getMethod = processLifecycleOwnerClass.getMethod("get")
|
|
|
- val lifecycleOwner = getMethod.invoke(null)
|
|
|
- val lifecycleField = lifecycleOwner.javaClass.getMethod("getLifecycle")
|
|
|
- val lifecycle = lifecycleField.invoke(lifecycleOwner)
|
|
|
- val addObserverMethod = lifecycle.javaClass.getMethod("addObserver", LifecycleObserver::class.java)
|
|
|
- addObserverMethod.invoke(lifecycle, this)
|
|
|
- LogHelper.d(TAG, "SocketIOManager 初始化完成")
|
|
|
+ ProcessLifecycleOwner.get().lifecycle.addObserver(this)
|
|
|
+ LogHelper.d(TAG, "生命周期监听已注册")
|
|
|
} catch (e: Exception) {
|
|
|
LogHelper.e(TAG, "注册生命周期监听失败", e)
|
|
|
}
|
|
|
+
|
|
|
+ // 如果用户已登录,自动连接
|
|
|
+ appScope.launch {
|
|
|
+ if (AuthManager.isLoggedIn()) {
|
|
|
+ LogHelper.d(TAG, "用户已登录,自动连接 Socket.IO...")
|
|
|
+ connect()
|
|
|
+ } else {
|
|
|
+ LogHelper.d(TAG, "用户未登录,等待登录成功后连接")
|
|
|
+ // 更新连接状态为 false(用户未登录)
|
|
|
+ _connectionState.emit(false)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ LogHelper.d(TAG, "SocketIOManager 初始化完成(自动处理连接和Token刷新)")
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * App 进入前台时调用
|
|
|
+ * 确保已连接(如果未连接则自动连接)
|
|
|
*
|
|
|
- * 检查 SocketIO 连接状态,如果断开则自动重连
|
|
|
- * 如果连接失败(可能是 Token 过期),会自动刷新 Token 后重连
|
|
|
+ * 在用户登录成功后调用,确保 Socket.IO 已连接
|
|
|
+ * 如果已连接,则不做任何操作
|
|
|
+ * 如果未连接,会自动处理连接(包括 Token 刷新)
|
|
|
+ *
|
|
|
+ * 使用场景:
|
|
|
+ * - 用户登录成功后调用
|
|
|
+ * - 应用启动后检查连接状态时调用
|
|
|
+ * - 前台重连时调用
|
|
|
*/
|
|
|
- @OnLifecycleEvent(androidx.lifecycle.Lifecycle.Event.ON_START)
|
|
|
- fun onAppForeground() {
|
|
|
- LogHelper.d(TAG, "App 进入前台,检查 SocketIO 连接状态")
|
|
|
+ fun ensureConnected() {
|
|
|
+ appScope.launch {
|
|
|
+ // 检查是否已登录
|
|
|
+ if (!AuthManager.isLoggedIn()) {
|
|
|
+ LogHelper.d(TAG, "用户未登录,无法连接")
|
|
|
+ _connectionState.emit(false)
|
|
|
+ return@launch
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果已连接,更新状态并返回
|
|
|
+ if (isConnected()) {
|
|
|
+ LogHelper.d(TAG, "已连接,状态正常")
|
|
|
+ _connectionState.emit(true)
|
|
|
+ return@launch
|
|
|
+ }
|
|
|
+
|
|
|
+ // 未连接,尝试重连
|
|
|
+ LogHelper.d(TAG, "检测到未连接,开始重连...")
|
|
|
+ checkAndReconnect()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 连接 Socket.IO(自动处理 Token 刷新)
|
|
|
+ */
|
|
|
+ private suspend fun connect() {
|
|
|
+ LogHelper.d(TAG, "=== Socket.IO 连接开始 ===")
|
|
|
|
|
|
- // 检查是否已登录
|
|
|
- if (!AuthManager.isLoggedIn()) {
|
|
|
- LogHelper.d(TAG, "用户未登录,跳过 SocketIO 重连")
|
|
|
+ // 获取服务器地址
|
|
|
+ val url = serverURL ?: ServerConfigManager.getSocketIOUrl()
|
|
|
+ if (url.isEmpty()) {
|
|
|
+ LogHelper.e(TAG, "服务器地址未配置,无法连接")
|
|
|
+ _connectionState.emit(false)
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- // 检查连接状态,如果断开则重连
|
|
|
+ this.serverURL = url
|
|
|
+
|
|
|
+ // 获取 Token,如果为空或过期则刷新
|
|
|
+ var token = StorageImpl.getString("access_token")
|
|
|
+
|
|
|
+ // 如果 Token 为空,尝试刷新
|
|
|
+ if (token.isEmpty()) {
|
|
|
+ LogHelper.d(TAG, "Token 为空,尝试刷新...")
|
|
|
+ token = refreshTokenIfNeeded() ?: ""
|
|
|
+ if (token.isEmpty()) {
|
|
|
+ LogHelper.e(TAG, "Token 为空且刷新失败,无法连接")
|
|
|
+ _connectionState.emit(false)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果 Token 已过期,尝试刷新
|
|
|
+ if (!JWTUtil.isTokenValid(token)) {
|
|
|
+ LogHelper.w(TAG, "Token 已过期,尝试刷新...")
|
|
|
+ val expiresAt = JWTUtil.getExpiresAt(token)
|
|
|
+ if (expiresAt != null) {
|
|
|
+ val expiresDate = java.util.Date(expiresAt * 1000) // 转换为毫秒
|
|
|
+ LogHelper.d(TAG, "Token 过期时间: $expiresDate")
|
|
|
+ }
|
|
|
+ token = refreshTokenIfNeeded() ?: token // 如果刷新失败,仍然尝试使用旧 Token(由服务端判断)
|
|
|
+
|
|
|
+ // 如果刷新后仍然无效,返回
|
|
|
+ if (token.isEmpty() || !JWTUtil.isTokenValid(token)) {
|
|
|
+ LogHelper.e(TAG, "Token 刷新失败或仍然无效,无法连接")
|
|
|
+ _connectionState.emit(false)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ LogHelper.d(TAG, "Token 刷新成功,使用新 Token 连接...")
|
|
|
+ }
|
|
|
+
|
|
|
+ LogHelper.d(TAG, "正在连接到服务器: $url")
|
|
|
+ socketClient.connect(url, token)
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 断开连接
|
|
|
+ */
|
|
|
+ fun disconnect() {
|
|
|
+ LogHelper.d(TAG, "断开 Socket.IO 连接")
|
|
|
+ socketClient.disconnect()
|
|
|
appScope.launch {
|
|
|
- try {
|
|
|
- // 使用反射获取 SocketIORepository,避免强依赖
|
|
|
- val factoryClass = Class.forName("com.narutohuo.xindazhou.socketio.factory.SocketIORepositoryFactory")
|
|
|
- val getInstanceMethod = factoryClass.getMethod("getInstance")
|
|
|
- val socketIORepository = getInstanceMethod.invoke(null)
|
|
|
-
|
|
|
- val socketUrl = ServerConfigManager.getSocketIOUrl()
|
|
|
- var token = AuthManager.getAccessToken()
|
|
|
-
|
|
|
- if (token.isNullOrEmpty()) {
|
|
|
- LogHelper.w(TAG, "Token 为空,无法重连 SocketIO")
|
|
|
- return@launch
|
|
|
+ _connectionState.emit(false)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查连接状态
|
|
|
+ */
|
|
|
+ fun isConnected(): Boolean {
|
|
|
+ return socketClient.isConnected()
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 自动重连(如果断开)
|
|
|
+ */
|
|
|
+ private suspend fun checkAndReconnect() {
|
|
|
+ if (isConnected()) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 防重复重连(5秒内最多重连一次)
|
|
|
+ val currentTime = System.currentTimeMillis()
|
|
|
+ if (isReconnecting || (currentTime - lastReconnectTime < 5000)) {
|
|
|
+ LogHelper.d(TAG, "正在重连或刚重连过,跳过")
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ isReconnecting = true
|
|
|
+ lastReconnectTime = currentTime
|
|
|
+
|
|
|
+ try {
|
|
|
+ LogHelper.d(TAG, "检测到断开连接,开始重连...")
|
|
|
+
|
|
|
+ // 如果未初始化,先初始化
|
|
|
+ if (!isInitialized) {
|
|
|
+ LogHelper.d(TAG, "检测到未初始化,先执行初始化...")
|
|
|
+ val app = application ?: run {
|
|
|
+ LogHelper.e(TAG, "Application 未设置,无法初始化")
|
|
|
+ return
|
|
|
}
|
|
|
-
|
|
|
- // 检查是否已连接
|
|
|
- val isConnectedMethod = socketIORepository.javaClass.getMethod("isConnected")
|
|
|
- val isConnected = isConnectedMethod.invoke(socketIORepository) as Boolean
|
|
|
-
|
|
|
- if (isConnected) {
|
|
|
- LogHelper.d(TAG, "SocketIO 已连接,无需重连")
|
|
|
- return@launch
|
|
|
+ initialize(app)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 刷新 Token 后重连
|
|
|
+ val newToken = refreshTokenIfNeeded()
|
|
|
+ if (!newToken.isNullOrEmpty()) {
|
|
|
+ LogHelper.d(TAG, "Token 已刷新,使用新 Token 重连...")
|
|
|
+ delay(1000) // 延迟1秒,避免过于频繁的重连尝试
|
|
|
+ connect()
|
|
|
+ } else {
|
|
|
+ LogHelper.e(TAG, "Token 刷新失败,无法重连")
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ isReconnecting = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 订阅消息(返回 SharedFlow)
|
|
|
+ *
|
|
|
+ * 各个业务模块使用此方法订阅需要的消息类型
|
|
|
+ *
|
|
|
+ * 注意:
|
|
|
+ * - 如果未连接,会自动尝试连接
|
|
|
+ * - 如果用户未登录,会等待登录成功后连接
|
|
|
+ *
|
|
|
+ * 示例:
|
|
|
+ * ```kotlin
|
|
|
+ * // CommunityViewModel.kt
|
|
|
+ * viewModelScope.launch {
|
|
|
+ * SocketIOManager.shared.subscribe("community_message").collect { response ->
|
|
|
+ * // 处理社区消息
|
|
|
+ * }
|
|
|
+ * }
|
|
|
+ * ```
|
|
|
+ */
|
|
|
+ fun subscribe(eventName: String): SharedFlow<SocketIOResponse> {
|
|
|
+ return eventFlows.getOrPut(eventName) {
|
|
|
+ MutableSharedFlow<SocketIOResponse>(extraBufferCapacity = 1).also { flow ->
|
|
|
+ // 订阅 Socket.IO 事件
|
|
|
+ socketClient.on(eventName) { response ->
|
|
|
+ appScope.launch {
|
|
|
+ flow.emit(response)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- // 先尝试连接
|
|
|
- LogHelper.d(TAG, "SocketIO 未连接,开始重连...")
|
|
|
- val checkAndReconnectMethod = socketIORepository.javaClass.getMethod(
|
|
|
- "checkAndReconnect",
|
|
|
- String::class.java,
|
|
|
- String::class.java
|
|
|
- )
|
|
|
- checkAndReconnectMethod.invoke(socketIORepository, socketUrl, token)
|
|
|
-
|
|
|
- // 等待 2 秒,检查连接是否成功
|
|
|
- delay(2000)
|
|
|
-
|
|
|
- // 如果连接仍然失败,可能是 Token 过期,尝试刷新 Token 后重连
|
|
|
- val isConnectedAfter = isConnectedMethod.invoke(socketIORepository) as Boolean
|
|
|
- if (!isConnectedAfter) {
|
|
|
- LogHelper.d(TAG, "连接失败,可能是 Token 过期,尝试刷新 Token 后重连")
|
|
|
- val refreshedToken = AuthManager.refreshTokenIfNeeded()
|
|
|
- if (!refreshedToken.isNullOrEmpty() && refreshedToken != token) {
|
|
|
- LogHelper.d(TAG, "Token 已刷新,使用新 Token 重连")
|
|
|
- checkAndReconnectMethod.invoke(socketIORepository, socketUrl, refreshedToken)
|
|
|
- } else {
|
|
|
- LogHelper.w(TAG, "Token 刷新失败或未刷新,无法重连")
|
|
|
- }
|
|
|
- } else {
|
|
|
- LogHelper.d(TAG, "SocketIO 重连成功")
|
|
|
+ // 如果未连接,尝试连接(使用 ensureConnected 统一处理)
|
|
|
+ if (!isConnected()) {
|
|
|
+ ensureConnected()
|
|
|
}
|
|
|
- } catch (e: ClassNotFoundException) {
|
|
|
- LogHelper.d(TAG, "SocketIO 模块未引入,跳过重连")
|
|
|
- } catch (e: Exception) {
|
|
|
- LogHelper.e(TAG, "SocketIO 重连失败", e)
|
|
|
}
|
|
|
+ }.asSharedFlow()
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 发送消息
|
|
|
+ */
|
|
|
+ fun emit(eventName: String, data: Any) {
|
|
|
+ if (!isConnected()) {
|
|
|
+ LogHelper.w(TAG, "Socket.IO 未连接,无法发送事件: $eventName,尝试重新连接...")
|
|
|
+ appScope.launch {
|
|
|
+ checkAndReconnect()
|
|
|
+ }
|
|
|
+ return
|
|
|
}
|
|
|
+
|
|
|
+ socketClient.emit(eventName, data)
|
|
|
+ }
|
|
|
+
|
|
|
+ // MARK: - 生命周期监听
|
|
|
+
|
|
|
+ /**
|
|
|
+ * App 进入前台时调用
|
|
|
+ *
|
|
|
+ * 检查 SocketIO 连接状态,如果断开则自动重连
|
|
|
+ * 确保应用回到前台时,Socket.IO 连接状态正常
|
|
|
+ */
|
|
|
+ override fun onStart(owner: LifecycleOwner) {
|
|
|
+ LogHelper.d(TAG, "App 进入前台,检查 SocketIO 连接状态")
|
|
|
+
|
|
|
+ // 使用 ensureConnected() 统一处理连接逻辑
|
|
|
+ ensureConnected()
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -149,10 +358,115 @@ object SocketIOManager : LifecycleObserver {
|
|
|
* 2. 用户可能需要在后台接收消息
|
|
|
* 3. 系统会在内存不足时自动清理
|
|
|
*/
|
|
|
- @OnLifecycleEvent(androidx.lifecycle.Lifecycle.Event.ON_STOP)
|
|
|
- fun onAppBackground() {
|
|
|
+ override fun onStop(owner: LifecycleOwner) {
|
|
|
LogHelper.d(TAG, "App 进入后台")
|
|
|
// 不断开连接,保持连接以便接收消息
|
|
|
}
|
|
|
+
|
|
|
+ // MARK: - Private Methods
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 订阅连接事件
|
|
|
+ */
|
|
|
+ private fun subscribeConnectionEvents() {
|
|
|
+ socketClient.on("connect") { response ->
|
|
|
+ appScope.launch {
|
|
|
+ _connectionState.emit(true)
|
|
|
+ LogHelper.d(TAG, "✅ Socket.IO 连接成功")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ socketClient.on("disconnect") { response ->
|
|
|
+ appScope.launch {
|
|
|
+ _connectionState.emit(false)
|
|
|
+ LogHelper.d(TAG, "❌ Socket.IO 断开连接: ${response.data}")
|
|
|
+
|
|
|
+ // 自动重连(延迟2秒)
|
|
|
+ delay(2000)
|
|
|
+ checkAndReconnect()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ socketClient.on("connect_error") { response ->
|
|
|
+ appScope.launch {
|
|
|
+ _connectionState.emit(false)
|
|
|
+ val errorMsg = response.data
|
|
|
+ LogHelper.e(TAG, "❌ Socket.IO 连接错误: $errorMsg")
|
|
|
+
|
|
|
+ // 如果是 Token 错误,刷新后重连
|
|
|
+ if (errorMsg.contains("token", ignoreCase = true) ||
|
|
|
+ errorMsg.contains("unauthorized", ignoreCase = true)) {
|
|
|
+ refreshTokenAndReconnect()
|
|
|
+ } else {
|
|
|
+ // 其他错误也尝试重连
|
|
|
+ delay(2000)
|
|
|
+ checkAndReconnect()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 刷新 Token 并重连
|
|
|
+ */
|
|
|
+ private suspend fun refreshTokenAndReconnect() {
|
|
|
+ val newToken = refreshTokenIfNeeded()
|
|
|
+ if (!newToken.isNullOrEmpty()) {
|
|
|
+ LogHelper.d(TAG, "Token 已刷新,使用新 Token 重连...")
|
|
|
+ delay(1000) // 延迟1秒
|
|
|
+ connect()
|
|
|
+ } else {
|
|
|
+ LogHelper.e(TAG, "Token 刷新失败,无法重连")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 刷新 Token(如果需要)
|
|
|
+ *
|
|
|
+ * 使用 AuthManager 处理 Token 刷新
|
|
|
+ */
|
|
|
+ private suspend fun refreshTokenIfNeeded(): String? {
|
|
|
+ // 防止短时间内多次刷新(5秒内最多刷新一次)
|
|
|
+ val currentTime = System.currentTimeMillis()
|
|
|
+ if (isRefreshingToken) {
|
|
|
+ LogHelper.d(TAG, "正在刷新 Token,等待...")
|
|
|
+ // 等待刷新完成,最多等待3秒
|
|
|
+ var waitCount = 0
|
|
|
+ while (isRefreshingToken && waitCount < 30) {
|
|
|
+ delay(100) // 0.1秒
|
|
|
+ waitCount++
|
|
|
+ }
|
|
|
+ // 刷新完成后,从存储中获取最新的 Token
|
|
|
+ return StorageImpl.getString("access_token").takeIf { it.isNotEmpty() }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (currentTime - lastTokenRefreshTime < 5000) {
|
|
|
+ LogHelper.d(TAG, "刚刷新过 Token(${(currentTime - lastTokenRefreshTime) / 1000}秒前),使用存储中的 Token")
|
|
|
+ return StorageImpl.getString("access_token").takeIf { it.isNotEmpty() }
|
|
|
+ }
|
|
|
+
|
|
|
+ return try {
|
|
|
+ isRefreshingToken = true
|
|
|
+ lastTokenRefreshTime = currentTime
|
|
|
+
|
|
|
+ LogHelper.d(TAG, "开始刷新 Token...")
|
|
|
+
|
|
|
+ // 使用 base-common 的 AuthManager
|
|
|
+ val newToken = AuthManager.refreshTokenIfNeeded()
|
|
|
+
|
|
|
+ if (!newToken.isNullOrEmpty()) {
|
|
|
+ LogHelper.d(TAG, "Token 刷新成功")
|
|
|
+ newToken
|
|
|
+ } else {
|
|
|
+ LogHelper.w(TAG, "Token 刷新失败(返回 null)")
|
|
|
+ null
|
|
|
+ }
|
|
|
+ } catch (e: Exception) {
|
|
|
+ LogHelper.e(TAG, "Token 刷新异常", e)
|
|
|
+ null
|
|
|
+ } finally {
|
|
|
+ isRefreshingToken = false
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|