From 410219f38225010da9c5b52922dbc8efad40eb74 Mon Sep 17 00:00:00 2001 From: wdvipa Date: Sun, 15 Feb 2026 14:57:38 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8DBitmap=E5=B9=B6?= =?UTF-8?q?=E5=8F=91=E5=9B=9E=E6=94=B6=E9=97=AA=E9=80=80=E5=92=8C=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E5=B8=A7=E7=AB=9E=E6=80=81=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - lastValidBitmap/lastCaptureTime添加@Volatile确保多协程可见性 - safeRecycleLastValidBitmap()添加@Synchronized防止并发double-recycle - 新增updateLastValidBitmap()原子替换缓存帧(synchronized) - 新增compressCachedFrame()安全压缩缓存帧(synchronized) - 新增safeCopyLastValidBitmap()安全复制缓存帧(synchronized) - 替换所有直接访问lastValidBitmap的代码为synchronized方法调用 - 涉及方法: startMediaProjectionCapture/startAccessibilityScreenCapture/handleAndroid11ScreenshotFailure/captureWithMediaProjection/forceRefreshAndroid15Images - 清理MainActivity和SocketIOManager中日志的emoji符号 --- .../main/java/com/hikoncont/MainActivity.kt | 4 +- .../hikoncont/manager/ScreenCaptureManager.kt | 193 ++++++++++-------- .../com/hikoncont/network/SocketIOManager.kt | 138 +++++++------ 3 files changed, 183 insertions(+), 152 deletions(-) diff --git a/app/src/main/java/com/hikoncont/MainActivity.kt b/app/src/main/java/com/hikoncont/MainActivity.kt index 652037e..a92368b 100644 --- a/app/src/main/java/com/hikoncont/MainActivity.kt +++ b/app/src/main/java/com/hikoncont/MainActivity.kt @@ -3729,11 +3729,11 @@ class MainActivity : AppCompatActivity() { // 检查MediaProjectionManager是否已初始化 if (mediaProjectionManager == null) { - Log.e(TAG, "❌ MediaProjectionManager未初始化,重新初始化") + Log.e(TAG, "MediaProjectionManager未初始化,重新初始化") mediaProjectionManager = getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager if (mediaProjectionManager == null) { - Log.e(TAG, "❌ 重新初始化MediaProjectionManager失败") + Log.e(TAG, "重新初始化MediaProjectionManager失败") return } } diff --git a/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt b/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt index b0606dc..8f6f0f0 100644 --- a/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt +++ b/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt @@ -52,9 +52,9 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { private var imageReader: android.media.ImageReader? = null private var mediaProjection: android.media.projection.MediaProjection? = null - // 图像缓存机制 - private var lastValidBitmap: Bitmap? = null - private var lastCaptureTime = 0L + // 图像缓存机制 - @Volatile确保多协程可见性 + @Volatile private var lastValidBitmap: Bitmap? = null + @Volatile private var lastCaptureTime = 0L private var lastScreenshotTime = 0L // 新增:记录上次截图时间,防止截图间隔太短 // 状态跟踪 @@ -159,12 +159,10 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { /** * 安全回收lastValidBitmap,防止多线程并发导致的native crash (SIGSEGV) * - * 问题根因:多个协程(startMediaProjectionCapture、startAccessibilityScreenCapture、 - * captureWithMediaProjection等)并发访问lastValidBitmap,可能导致: - * 1. double-recycle:Bitmap已被其他线程recycle但引用未置null - * 2. Hardware Bitmap的底层HardwareBuffer已被释放 - * 两种情况都会在native层BitmapWrapper::freePixels()触发SIGSEGV + * 使用synchronized确保原子性:先置null再recycle, + * 防止两个协程同时拿到同一个引用并double-recycle */ + @Synchronized private fun safeRecycleLastValidBitmap() { val bitmapToRecycle = lastValidBitmap lastValidBitmap = null // 先置null,防止其他线程访问 @@ -178,6 +176,63 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { } } + /** + * 安全更新lastValidBitmap缓存 + * synchronized确保与safeRecycleLastValidBitmap互斥 + */ + @Synchronized + private fun updateLastValidBitmap(newBitmap: Bitmap) { + val old = lastValidBitmap + lastValidBitmap = newBitmap + trackBitmap(newBitmap) + lastCaptureTime = System.currentTimeMillis() + // 回收旧的缓存 + try { + if (old != null && !old.isRecycled) { + old.recycle() + } + } catch (e: Exception) { + Log.w(TAG, "回收旧缓存Bitmap异常: ${e.message}") + } + } + + /** + * 安全压缩缓存帧并返回JPEG数据 + * synchronized确保读取期间lastValidBitmap不被回收 + * @param maxAge 缓存最大有效期(ms) + * @return JPEG数据,缓存无效时返回null + */ + @Synchronized + private fun compressCachedFrame(maxAge: Long): ByteArray? { + val cached = lastValidBitmap ?: return null + if (cached.isRecycled) return null + val age = System.currentTimeMillis() - lastCaptureTime + if (age > maxAge) return null + return try { + compressBitmap(cached) + } catch (e: Exception) { + Log.w(TAG, "压缩缓存帧失败: ${e.message}") + null + } + } + + /** + * 安全复制lastValidBitmap + * synchronized确保复制期间不被其他线程回收 + * @return Bitmap副本,缓存无效时返回null + */ + @Synchronized + private fun safeCopyLastValidBitmap(): Bitmap? { + val cached = lastValidBitmap ?: return null + if (cached.isRecycled) return null + return try { + cached.copy(cached.config ?: Bitmap.Config.ARGB_8888, false) + } catch (e: Exception) { + Log.w(TAG, "复制缓存Bitmap失败: ${e.message}") + null + } + } + /** * 安全回收任意Bitmap,防止native crash */ @@ -484,10 +539,11 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { // 有效帧:发送并更新缓存 sendFrameToServer(jpegData) - safeRecycleLastValidBitmap() - lastValidBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, false) - lastValidBitmap?.let { trackBitmap(it) } - lastCaptureTime = System.currentTimeMillis() + // 安全更新缓存帧 + val copy = bitmap.copy(Bitmap.Config.ARGB_8888, false) + if (copy != null) { + updateLastValidBitmap(copy) + } consecutiveFailures = 0 consecutiveBlackFrames = 0 @@ -513,20 +569,10 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { } // Send cached valid frame to maintain continuity (if available) - val cachedBmp = lastValidBitmap - if (cachedBmp != null && !cachedBmp.isRecycled) { - val cacheAge = System.currentTimeMillis() - lastCaptureTime - if (cacheAge < 10000) { - try { - val cachedJpeg = compressBitmap(cachedBmp) - if (cachedJpeg.size >= MIN_VALID_FRAME_SIZE) { - sendFrameToServer(cachedJpeg) - Log.d(TAG, "Used cached frame instead of black frame (${cacheAge}ms ago)") - } - } catch (e: Exception) { - Log.w(TAG, "Failed to compress cached frame: ${e.message}") - } - } + val cachedJpeg = compressCachedFrame(10000) + if (cachedJpeg != null && cachedJpeg.size >= MIN_VALID_FRAME_SIZE) { + sendFrameToServer(cachedJpeg) + Log.d(TAG, "Used cached frame instead of black frame") } } @@ -539,20 +585,10 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { consecutiveFailures++ // Send cached frame when no new frame available - val cachedBmp2 = lastValidBitmap - if (cachedBmp2 != null && !cachedBmp2.isRecycled) { - val cacheAge = System.currentTimeMillis() - lastCaptureTime - if (cacheAge < 10000) { - try { - val cachedJpeg = compressBitmap(cachedBmp2) - if (cachedJpeg.size >= MIN_VALID_FRAME_SIZE) { - sendFrameToServer(cachedJpeg) - Log.d(TAG, "No new frame, sent cached frame (${cacheAge}ms ago)") - } - } catch (e: Exception) { - Log.w(TAG, "Failed to compress cached frame: ${e.message}") - } - } + val cachedJpeg2 = compressCachedFrame(10000) + if (cachedJpeg2 != null && cachedJpeg2.size >= MIN_VALID_FRAME_SIZE) { + sendFrameToServer(cachedJpeg2) + Log.d(TAG, "No new frame, sent cached frame") } // 连续失败过多,尝试重建 VirtualDisplay @@ -662,12 +698,11 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { // 缓存成功的截图,用于防止闪烁 try { - // 清理旧的缓存 - safeRecycleLastValidBitmap() - // 保存当前成功的截图副本 - lastValidBitmap = screenshot.copy(screenshot.config ?: Bitmap.Config.ARGB_8888, false) - lastCaptureTime = System.currentTimeMillis() - Log.d(TAG, "已缓存有效截图用于防闪烁: ${lastValidBitmap?.width}x${lastValidBitmap?.height}") + val copy = screenshot.copy(screenshot.config ?: Bitmap.Config.ARGB_8888, false) + if (copy != null) { + updateLastValidBitmap(copy) + Log.d(TAG, "已缓存有效截图用于防闪烁: ${copy.width}x${copy.height}") + } } catch (e: Exception) { Log.w(TAG, "缓存有效截图失败", e) } @@ -719,32 +754,20 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { Log.d(TAG, "Android 11+设备:截图失败 (连续${android11ConsecutiveFailures}次)") - // Prefer cached last frame to avoid flicker - val cachedBmp = lastValidBitmap - if (cachedBmp != null && !cachedBmp.isRecycled) { + // 安全读取缓存帧(synchronized保护) + val cachedCopy = safeCopyLastValidBitmap() + if (cachedCopy != null) { val cacheAge = currentTime - lastCaptureTime - - // Cache is fresh (within 10s), use it directly if (cacheAge < 10000) { - Log.d(TAG, "Android 11+ device: returning cached screenshot (${cacheAge}ms ago)") - return try { - cachedBmp.copy(cachedBmp.config ?: Bitmap.Config.ARGB_8888, false) - } catch (e: Exception) { - Log.w(TAG, "Failed to copy cached screenshot", e) - null - } + Log.d(TAG, "Android 11+: 返回缓存截图(${cacheAge}ms前)") + return cachedCopy } - - // Cache is older but still usable (within 60s), use during short failures if (cacheAge < 60000 && android11ConsecutiveFailures < 20) { - Log.d(TAG, "Android 11+ device: returning older cached screenshot (${cacheAge}ms ago)") - return try { - cachedBmp.copy(cachedBmp.config ?: Bitmap.Config.ARGB_8888, false) - } catch (e: Exception) { - Log.w(TAG, "Failed to copy cached screenshot", e) - null - } + Log.d(TAG, "Android 11+: 返回较旧缓存截图(${cacheAge}ms前)") + return cachedCopy } + // 缓存过旧,回收copy + safeRecycleBitmap(cachedCopy) } // 无可用缓存或缓存过旧,智能判断是否应该进入测试模式 @@ -1038,10 +1061,10 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { consecutiveImageFailures = 0 } - safeRecycleLastValidBitmap() - lastValidBitmap = newBitmap.copy(Bitmap.Config.ARGB_8888, false) - lastValidBitmap?.let { trackBitmap(it) } - lastCaptureTime = currentTime + val copy = newBitmap.copy(Bitmap.Config.ARGB_8888, false) + if (copy != null) { + updateLastValidBitmap(copy) + } return newBitmap } @@ -1063,19 +1086,15 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { } // If no new image available, check if cache can be used - val cachedBmp3 = lastValidBitmap - if (cachedBmp3 != null && !cachedBmp3.isRecycled) { + val cachedCopy3 = safeCopyLastValidBitmap() + if (cachedCopy3 != null) { val timeSinceLastCapture = currentTime - lastCaptureTime if (timeSinceLastCapture < 30000) { Log.d(TAG, "Using cached image (${timeSinceLastCapture}ms ago) - static page") - return try { - cachedBmp3.copy(Bitmap.Config.ARGB_8888, false) - } catch (e: Exception) { - Log.w(TAG, "Failed to copy cached bitmap: ${e.message}") - null - } + return cachedCopy3 } else { Log.w(TAG, "Cached image expired (${timeSinceLastCapture}ms ago), cleaning") + safeRecycleBitmap(cachedCopy3) safeRecycleLastValidBitmap() } } @@ -1115,9 +1134,10 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { val retryBitmap = convertImageToBitmap(retryImage) if (retryBitmap != null) { Log.i(TAG, "Android 15重新初始化后成功获取图像") - safeRecycleLastValidBitmap() - lastValidBitmap = retryBitmap.copy(Bitmap.Config.ARGB_8888, false) - lastCaptureTime = System.currentTimeMillis() + val retryCopy = retryBitmap.copy(Bitmap.Config.ARGB_8888, false) + if (retryCopy != null) { + updateLastValidBitmap(retryCopy) + } return retryBitmap } } catch (e: Exception) { @@ -2615,10 +2635,11 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { if (bitmap != null) { Log.i(TAG, "Android 15强制刷新成功:${bitmap.width}x${bitmap.height}") - // 更新缓存 - safeRecycleLastValidBitmap() - lastValidBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, false) - lastCaptureTime = System.currentTimeMillis() + // 安全更新缓存 + val copy = bitmap.copy(Bitmap.Config.ARGB_8888, false) + if (copy != null) { + updateLastValidBitmap(copy) + } return bitmap } diff --git a/app/src/main/java/com/hikoncont/network/SocketIOManager.kt b/app/src/main/java/com/hikoncont/network/SocketIOManager.kt index ea4a79e..c3634fa 100644 --- a/app/src/main/java/com/hikoncont/network/SocketIOManager.kt +++ b/app/src/main/java/com/hikoncont/network/SocketIOManager.kt @@ -342,9 +342,9 @@ class SocketIOManager(private val service: AccessibilityRemoteService) { socket?.let { socket -> socket.on(Socket.EVENT_CONNECT) { - Log.e(TAG, "✅✅✅ Socket.IO v4 连接成功!!! ✅✅✅") + Log.i(TAG, "Socket.IO v4 连接成功") isConnected = true - isDeviceRegistered = false // ✅ 重置注册状态,等待重新注册 + isDeviceRegistered = false // 重置注册状态,等待重新注册 // ✅ 记录连接成功时间和网络类型 lastConnectTime = System.currentTimeMillis() @@ -383,7 +383,7 @@ class SocketIOManager(private val service: AccessibilityRemoteService) { socket.on(Socket.EVENT_DISCONNECT) { args -> val reason = if (args.isNotEmpty()) args[0].toString() else "unknown" - Log.e(TAG, "📴📴📴 Socket.IO v4 断开: $reason 📴📴📴") + Log.w(TAG, "Socket.IO v4 断开: $reason") // ✅ 增强断开原因分析和统计 val currentTime = System.currentTimeMillis() @@ -426,7 +426,7 @@ class SocketIOManager(private val service: AccessibilityRemoteService) { socket.on(Socket.EVENT_CONNECT_ERROR) { args -> val error = if (args.isNotEmpty()) args[0].toString() else "unknown" - Log.e(TAG, "❌ Socket.IO v4 连接错误: $error") + Log.e(TAG, "Socket.IO v4 连接错误: $error") connectionFailureCount++ updateNetworkQualityScore(false, "connect_error", 0) } @@ -684,14 +684,14 @@ class SocketIOManager(private val service: AccessibilityRemoteService) { // 🔧 大小检查:避免发送过大数据导致transport error if (frameData.size > maxScreenDataSize) { - Log.w(TAG, "⚠️ 屏幕数据过大被跳过: ${frameData.size} bytes > ${maxScreenDataSize} bytes") + Log.w(TAG, "屏幕数据过大被跳过: ${frameData.size} bytes > ${maxScreenDataSize} bytes") return } // 🔧 连接饥饿检查:如果长时间无法发送数据,可能连接有问题 if (lastSuccessfulDataSend > 0 && currentTime - lastSuccessfulDataSend > dataStarvationTimeout) { if (!isDataStarved) { - Log.w(TAG, "⚠️ 检测到数据发送饥饿(${currentTime - lastSuccessfulDataSend}ms),连接可能有问题") + Log.w(TAG, "检测到数据发送饥饿(${currentTime - lastSuccessfulDataSend}ms),连接可能有问题") isDataStarved = true // 不立即重连,而是给连接一些时间恢复 } @@ -724,19 +724,17 @@ class SocketIOManager(private val service: AccessibilityRemoteService) { // 🎯 优化:记录压缩和编码效率,监控优化效果 val base64Size = base64Data.length val overhead = if (frameData.size > 0) "%.1f%%".format(((base64Size - frameData.size).toFloat() / frameData.size) * 100) else "N/A" - Log.d(TAG, "✅ 发送屏幕数据: JPEG${frameData.size}B -> Base64${base64Size}B (+$overhead 开销, 间隔${currentTime - (lastScreenDataTime - screenDataInterval)}ms, isLocked=${isScreenLocked})") + Log.d(TAG, "发送屏幕数据: JPEG${frameData.size}B -> Base64${base64Size}B (+$overhead 开销, isLocked=${isScreenLocked})") } catch (emitException: Exception) { screenDataFailureCount++ - Log.e(TAG, "❌ 发送屏幕数据失败(${screenDataFailureCount}次): ${emitException.message}") + Log.e(TAG, "发送屏幕数据失败(${screenDataFailureCount}次): ${emitException.message}") - // 🔧 大幅提高重连阈值,避免服务端正常丢弃数据被误判为网络错误 if (screenDataFailureCount >= 20) { - Log.e(TAG, "❌❌❌ 屏幕数据发送连续失败${screenDataFailureCount}次,触发重连检测!!! ❌❌❌") + Log.e(TAG, "屏幕数据发送连续失败${screenDataFailureCount}次,触发重连检测") checkConnectionAndReconnect() - screenDataFailureCount = 0 // 重置计数 + screenDataFailureCount = 0 } - // 🔧 不再抛出异常,避免ScreenCaptureManager误判为发送失败 - Log.w(TAG, "⚠️ 屏幕数据发送失败,但不影响后续发送") + Log.w(TAG, "屏幕数据发送失败,但不影响后续发送") } } else { @@ -1806,109 +1804,118 @@ class SocketIOManager(private val service: AccessibilityRemoteService) { private fun checkConnectionAndReconnect() { scope.launch { try { - Log.e(TAG, "🔍🔍🔍 立即检测连接状态!!! 🔍🔍🔍") + Log.d(TAG, "立即检测连接状态") - // 检查Socket连接状态 val socketConnected = socket?.connected() == true if (!socketConnected || !isConnected) { - Log.e(TAG, "❌❌❌ 连接已断开,立即重连!!! ❌❌❌") + Log.w(TAG, "连接已断开,立即重连") forceReconnect() } else { - Log.w(TAG, "✅✅✅ 连接状态正常 ✅✅✅") + Log.d(TAG, "连接状态正常") } } catch (e: Exception) { - Log.e(TAG, "❌❌❌ 连接检测异常!!! ❌❌❌", e) + Log.e(TAG, "连接检测异常", e) } } } /** - * ✅ 智能重新连接 - 增强稳定性版本(针对transport error问题) + * 智能重新连接 - 增强稳定性版本(针对transport error问题) + * 添加重连深度限制,防止无限递归导致协程泄漏和内存耗尽 */ + private var forceReconnectDepth = 0 + private val MAX_FORCE_RECONNECT_DEPTH = 3 + fun forceReconnect() { scope.launch { try { - Log.e(TAG, "🚀🚀🚀 开始智能重连(针对transport error优化)!!! 🚀🚀🚀") + forceReconnectDepth++ + if (forceReconnectDepth > MAX_FORCE_RECONNECT_DEPTH) { + Log.e(TAG, "重连深度超限(${forceReconnectDepth}/${MAX_FORCE_RECONNECT_DEPTH}),停止递归重连,等待Socket.IO自动重连") + forceReconnectDepth = 0 + return@launch + } + + Log.i(TAG, "开始智能重连(深度${forceReconnectDepth}/${MAX_FORCE_RECONNECT_DEPTH})") // 重置所有状态 isConnected = false isDeviceRegistered = false connectionCheckJob?.cancel() - // ✅ 优雅断开旧连接,给系统清理时间 + // 优雅断开旧连接,给系统清理时间 try { socket?.disconnect() - delay(1000) // 等待断开完成 + delay(1000) socket?.close() } catch (e: Exception) { Log.w(TAG, "断开旧连接时出现异常(可忽略)", e) } - Log.e(TAG, "🔄 旧连接已断开,等待智能延迟后重新连接...") + Log.i(TAG, "旧连接已断开,等待智能延迟后重新连接...") - // ✅ 智能延迟:根据网络环境调整等待时间 + 随机化避免多设备同时重连 + // 智能延迟:根据网络环境调整等待时间 + 随机化避免多设备同时重连 val baseDelay = if (Build.VERSION.SDK_INT >= 35) { - 8000L // Android 15需要更长恢复时间 + 8000L } else { - 5000L // 其他版本5秒 + 5000L } - val randomDelay = (1000..4000).random().toLong() // 1-4秒随机延迟 - val totalDelay = baseDelay + randomDelay + (transportErrorCount * 2000L) // 根据错误次数增加延迟 + val randomDelay = (1000..4000).random().toLong() + // 限制transportErrorCount对延迟的影响,防止延迟无限增长 + val errorIncrement = minOf(transportErrorCount, 5) * 2000L + val totalDelay = baseDelay + randomDelay + errorIncrement - Log.i(TAG, "🔄 智能重连延迟: ${totalDelay}ms (基础: ${baseDelay}ms + 随机: ${randomDelay}ms + 错误增量: ${transportErrorCount * 2000L}ms)") + Log.i(TAG, "智能重连延迟: ${totalDelay}ms (基础: ${baseDelay}ms + 随机: ${randomDelay}ms + 错误增量: ${errorIncrement}ms)") delay(totalDelay) - // ✅ 重新创建Socket实例,使用增强配置 - Log.e(TAG, "🔄 重新创建增强Socket实例...") + // 重新创建Socket实例,使用增强配置 + Log.i(TAG, "重新创建增强Socket实例...") try { - // ✅ 根据transport error历史决定使用的策略 val useConservativeStrategy = shouldUseConservativeReconnect() - Log.i(TAG, "📊 重连策略选择: 保守策略=$useConservativeStrategy, transportErrorCount=$transportErrorCount") + Log.i(TAG, "重连策略选择: 保守策略=$useConservativeStrategy, transportErrorCount=$transportErrorCount") val options = IO.Options().apply { - // ✅ 针对transport error的保守配置 + 网络质量自适应 val isVeryPoorNetwork = networkQualityScore < 30 timeout = when { - isVeryPoorNetwork -> 90000 // 网络很差时使用90秒超时 - useConservativeStrategy -> 60000 // 保守策略使用60秒超时 - else -> 45000 // 标准45秒超时 + isVeryPoorNetwork -> 90000 + useConservativeStrategy -> 60000 + else -> 45000 } reconnection = true reconnectionAttempts = when { - isVeryPoorNetwork -> 3 // 网络很差时只重试3次 - useConservativeStrategy -> 5 // 保守策略重试5次 - else -> 10 // 标准重试10次 + isVeryPoorNetwork -> 3 + useConservativeStrategy -> 5 + else -> 10 + } + reconnectionDelay = when { + isVeryPoorNetwork -> 8000L + useConservativeStrategy -> 5000L + else -> 3000L + } + reconnectionDelayMax = when { + isVeryPoorNetwork -> 30000L + useConservativeStrategy -> 20000L + else -> 12000L } - reconnectionDelay = when { - isVeryPoorNetwork -> 8000L // 网络很差时延迟8秒 - useConservativeStrategy -> 5000L // 保守策略延迟5秒 - else -> 3000L // 标准延迟3秒 - } - reconnectionDelayMax = when { - isVeryPoorNetwork -> 30000L // 网络很差时最大延迟30秒 - useConservativeStrategy -> 20000L // 保守策略最大延迟20秒 - else -> 12000L // 标准最大延迟12秒 - } - // ✅ 传输策略:根据网络质量和历史情况调整 + // 传输策略:根据网络质量和历史情况调整 transports = when { isVeryPoorNetwork || networkQualityScore < 40 -> { - arrayOf("polling") // 网络质量差时只使用轮询,更稳定 + arrayOf("polling") } useConservativeStrategy -> { - arrayOf("polling") // 保守策略只使用轮询 + arrayOf("polling") } else -> { - arrayOf("polling", "websocket") // 标准策略先轮询再websocket + arrayOf("polling", "websocket") } } - upgrade = !useConservativeStrategy // 保守策略禁用升级 + upgrade = !useConservativeStrategy rememberUpgrade = true - forceNew = true // 强制创建新连接 + forceNew = true - // ✅ 添加重连标识,便于服务器识别 query = "reconnect=true&strategy=${if (useConservativeStrategy) "conservative" else "standard"}×tamp=${System.currentTimeMillis()}" } @@ -1916,20 +1923,23 @@ class SocketIOManager(private val service: AccessibilityRemoteService) { setupEventListeners() socket?.connect() - Log.e(TAG, "🔄🔄🔄 增强Socket实例已创建并连接!!! 🔄🔄🔄") + Log.i(TAG, "增强Socket实例已创建并连接") + // 重连成功,重置深度计数 + forceReconnectDepth = 0 } catch (e: Exception) { - Log.e(TAG, "❌❌❌ 重新创建Socket失败!!! ❌❌❌", e) + Log.e(TAG, "重新创建Socket失败", e) - // ✅ 重连失败时的回退策略 - delay(10000) // 等待10秒后再次尝试 + // 重连失败时的回退策略,有深度限制保护 + delay(10000) if (!isConnected) { - Log.w(TAG, "🔄 首次重连失败,10秒后再次尝试...") - forceReconnect() // 递归重试,但有延迟保护 + Log.w(TAG, "重连失败,10秒后再次尝试(深度${forceReconnectDepth}/${MAX_FORCE_RECONNECT_DEPTH})...") + forceReconnect() } } } catch (e: Exception) { - Log.e(TAG, "❌❌❌ 智能重连异常!!! ❌❌❌", e) + Log.e(TAG, "智能重连异常", e) + forceReconnectDepth = 0 } } }