diff --git a/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt b/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt index 83b06a1..d8b2ef0 100644 --- a/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt +++ b/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt @@ -83,7 +83,11 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { // ✅ 图像获取失败计数 private var consecutiveImageFailures = 0 - private val maxImageFailuresBeforeRecreation = 10 // 连续10次失败才考虑重新创建 + private val maxImageFailuresBeforeRecreation = 5 // 连续5次失败即考虑重新创建(从10降低,加速回退) + + // ✅ VirtualDisplay重建次数限制,防止无限重建循环 + private var virtualDisplayRebuildCount = 0 + private val MAX_VIRTUAL_DISPLAY_REBUILD_ATTEMPTS = 3 // 最多重建3次,之后直接回退到无障碍截图 // 🔑 新增:AccessibilityService截图模式开关 private var useAccessibilityScreenshot = false @@ -360,7 +364,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { captureScope.launch { // 先确保 MediaProjection 可用 if (!ensureMediaProjection()) { - Log.e(TAG, "❌ MediaProjection 不可用,回退到无障碍截图") + Log.w(TAG, "⚠️ MediaProjection 不可用,回退到无障碍截图") // 回退到无障碍截图作为兜底 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { enableAccessibilityScreenshotMode() @@ -377,7 +381,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { } if (virtualDisplay == null) { - Log.e(TAG, "❌ VirtualDisplay 创建失败,回退到无障碍截图") + Log.w(TAG, "⚠️ VirtualDisplay 创建失败,回退到无障碍截图") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { enableAccessibilityScreenshotMode() startAccessibilityScreenCapture() @@ -390,6 +394,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { Log.i(TAG, "✅ MediaProjection VirtualDisplay 就绪,开始连续帧采集循环") var consecutiveFailures = 0 + virtualDisplayRebuildCount = 0 // 重置重建计数 // ✅ 最小有效帧大小阈值:正常480×854 JPEG即使最低质量也>5KB // 低于此值的帧几乎肯定是黑屏/空白帧(VirtualDisplay未刷新) @@ -400,6 +405,25 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { val MAX_BLACK_FRAMES_BEFORE_FALLBACK = 30 // 连续30个黑屏帧后切换到无障碍截图 while (isCapturing) { + // ✅ BufferQueue abandoned 检测:Surface失效时立即回退,避免大量错误日志 + val surfaceValid = try { + imageReader?.surface?.isValid == true + } catch (e: Exception) { + Log.w(TAG, "⚠️ Surface有效性检查异常: ${e.message}") + false + } + if (!surfaceValid) { + Log.w(TAG, "🚨 Surface已失效(BufferQueue abandoned),停止MediaProjection采集") + cleanupVirtualDisplayOnly() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + Log.i(TAG, "🔄 回退到无障碍截图模式") + enableAccessibilityScreenshotMode() + startAccessibilityScreenCapture() + } else { + startFallbackCapture() + } + return@launch + } try { val image = imageReader?.acquireLatestImage() if (image != null) { @@ -476,14 +500,38 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { // 连续失败过多,尝试重建 VirtualDisplay if (consecutiveFailures >= maxImageFailuresBeforeRecreation) { - Log.w(TAG, "⚠️ 连续 ${consecutiveFailures} 次无法获取有效帧,尝试重建 VirtualDisplay") + virtualDisplayRebuildCount++ + Log.w(TAG, "⚠️ 连续 ${consecutiveFailures} 次无法获取有效帧,尝试重建 VirtualDisplay (第${virtualDisplayRebuildCount}/${MAX_VIRTUAL_DISPLAY_REBUILD_ATTEMPTS}次)") + + // ✅ 重建次数超限,直接回退到无障碍截图 + if (virtualDisplayRebuildCount > MAX_VIRTUAL_DISPLAY_REBUILD_ATTEMPTS) { + Log.w(TAG, "🚨 VirtualDisplay重建次数已达上限(${MAX_VIRTUAL_DISPLAY_REBUILD_ATTEMPTS}),MediaProjection在此设备不可靠") + cleanupVirtualDisplayOnly() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + Log.i(TAG, "🔄 回退到无障碍截图模式") + enableAccessibilityScreenshotMode() + startAccessibilityScreenCapture() + } else { + startFallbackCapture() + } + return@launch + } + cleanupVirtualDisplayOnly() delay(500) setupMediaProjectionResources() consecutiveFailures = 0 - if (virtualDisplay == null) { - Log.e(TAG, "❌ VirtualDisplay 重建失败,回退到无障碍截图") + // ✅ 重建后验证Surface有效性 + val rebuildSurfaceValid = try { + imageReader?.surface?.isValid == true + } catch (e: Exception) { + false + } + + if (virtualDisplay == null || !rebuildSurfaceValid) { + Log.w(TAG, "⚠️ VirtualDisplay 重建失败或Surface无效,回退到无障碍截图") + cleanupVirtualDisplayOnly() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { enableAccessibilityScreenshotMode() startAccessibilityScreenCapture() @@ -496,8 +544,10 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { // 按动态帧率控制采集间隔 delay(1000L / dynamicFps) + } catch (e: CancellationException) { + throw e // 协程正常取消,向上传播 } catch (e: Exception) { - Log.e(TAG, "MediaProjection 帧采集异常", e) + Log.w(TAG, "⚠️ MediaProjection 帧采集异常: ${e.message}") consecutiveFailures++ delay(100) } @@ -535,7 +585,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { } } - Log.e(TAG, "❌ 无法获取 MediaProjection,权限可能未授予") + Log.w(TAG, "⚠️ 无法获取 MediaProjection,权限可能未授予") return false } @@ -606,8 +656,10 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { delay(50) // 失败时50ms后重试,让系统截图间隔限制自己控制节奏 } + } catch (e: CancellationException) { + throw e // 协程正常取消,向上传播 } catch (e: Exception) { - Log.e(TAG, "屏幕捕获失败", e) + Log.w(TAG, "⚠️ 屏幕捕获失败: ${e.message}") consecutiveFailures++ delay(100) // ✅ 出错时进一步缩短间隔,保持流畅度(从200ms改为100ms) } @@ -778,7 +830,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { Log.w(TAG, "⚠️ ScreenshotResult中的HardwareBuffer为null") // 🚨 Android 11+特殊处理:记录详细错误信息 if (Build.VERSION.SDK_INT >= 30) { - Log.e(TAG, "📱 Android 11+设备:HardwareBuffer为null,可能是权限问题") + Log.w(TAG, "📱 Android 11+设备:HardwareBuffer为null,可能是权限问题") } } } else { @@ -789,7 +841,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { Log.e(TAG, "处理截图结果失败", e) // 🚨 Android 11+特殊处理:记录详细异常信息 if (Build.VERSION.SDK_INT >= 30) { - Log.e(TAG, "📱 Android 11+设备:处理截图结果时发生异常", e) + Log.d(TAG, "📱 Android 11+设备:处理截图结果时发生异常: ${e.message}") } } finally { latch.countDown() @@ -797,7 +849,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { } override fun onFailure(failureErrorCode: Int) { - Log.e(TAG, "无障碍服务截图失败,错误码: $failureErrorCode") + Log.w(TAG, "无障碍服务截图失败,错误码: $failureErrorCode") errorCode = failureErrorCode // 🚨 Android 11+特殊处理:详细分析错误码 @@ -809,7 +861,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { android.accessibilityservice.AccessibilityService.ERROR_TAKE_SCREENSHOT_NO_ACCESSIBILITY_ACCESS -> "无障碍权限不足" else -> "未知错误($failureErrorCode)" } - Log.e(TAG, "📱 Android 11+设备:截图失败详情 - $errorMessage") + Log.w(TAG, "📱 Android 11+设备:截图失败详情 - $errorMessage") } latch.countDown() @@ -820,7 +872,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { Log.e(TAG, "调用takeScreenshot失败", e) // 🚨 Android 11+特殊处理:记录调用失败详情 if (Build.VERSION.SDK_INT >= 30) { - Log.e(TAG, "📱 Android 11+设备:调用takeScreenshot时发生异常", e) + Log.d(TAG, "📱 Android 11+设备:调用takeScreenshot时发生异常: ${e.message}") } latch.countDown() } @@ -865,7 +917,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { Log.e(TAG, "无障碍服务截图异常", e) // 🚨 Android 11+特殊处理:异常时返回null让主循环处理 if (Build.VERSION.SDK_INT >= 30) { - Log.e(TAG, "📱 Android 11+设备:无障碍截图异常,返回null让主循环处理", e) + Log.d(TAG, "📱 Android 11+设备:无障碍截图异常,返回null让主循环处理: ${e.message}") return null } // 出现异常时回退到MediaProjection @@ -910,13 +962,13 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { Log.i(TAG, "✅ Android 15静默恢复成功,继续屏幕捕获") } else { // 静默恢复失败,可能确实需要重新申请权限 - Log.e(TAG, "❌ Android 15静默恢复失败,MediaProjection权限确实丢失") + Log.w(TAG, "⚠️ Android 15静默恢复失败,MediaProjection权限确实丢失") triggerPermissionRecovery() return null } } else { // 其他版本的处理逻辑 - Log.e(TAG, "❌ MediaProjection权限丢失,触发自动权限恢复") + Log.w(TAG, "⚠️ MediaProjection权限丢失,触发自动权限恢复") triggerPermissionRecovery() return null } @@ -1159,6 +1211,19 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { if (virtualDisplay != null) { Log.i(TAG, "VirtualDisplay创建成功") + // ✅ 创建后立即验证Surface有效性,防止BufferQueue abandoned + val surfaceValid = try { + imageReader?.surface?.isValid == true + } catch (e: Exception) { + Log.w(TAG, "⚠️ Surface有效性验证异常: ${e.message}") + false + } + if (!surfaceValid) { + Log.e(TAG, "🚨 VirtualDisplay创建后Surface已失效,清理资源") + cleanupVirtualDisplayOnly() + return + } + // ✅ Android 15:VirtualDisplay创建成功后触发权限申请处理 if (Build.VERSION.SDK_INT >= 35) { Log.i(TAG, "🔧 Android 15:VirtualDisplay创建成功,触发权限申请处理") @@ -1379,7 +1444,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { Log.i(TAG, "🔒 已标记Android 15 session为已使用") } } catch (e: Exception) { - Log.e(TAG, "标记Android 15 session失败", e) + Log.w(TAG, "⚠️ 标记Android 15 session失败", e) } } @@ -1402,7 +1467,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { } } catch (e: Exception) { - Log.e(TAG, "通知MediaProjection权限丢失失败", e) + Log.w(TAG, "⚠️ 通知MediaProjection权限丢失失败", e) } } @@ -1425,7 +1490,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { Log.i(TAG, "屏幕捕获已暂停,权限保留,状态已持久化") } catch (e: Exception) { - Log.e(TAG, "暂停屏幕捕获失败", e) + Log.w(TAG, "⚠️ 暂停屏幕捕获失败", e) } } @@ -1448,7 +1513,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { startCapture() } catch (e: Exception) { - Log.e(TAG, "恢复屏幕捕获失败", e) + Log.w(TAG, "⚠️ 恢复屏幕捕获失败", e) } } @@ -1584,7 +1649,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { Log.d(TAG, "🎯 智能压缩完成: 原始${bitmap.width}x${bitmap.height} -> 输出${compressedData.size} bytes (质量$quality, 尝试${attempts}次)") } catch (e: Exception) { - Log.e(TAG, "❌ 智能压缩失败,使用基础压缩", e) + Log.w(TAG, "⚠️ 智能压缩失败,使用基础压缩", e) // 如果智能压缩失败,使用基础压缩 outputStream.reset() bitmap.compress(Bitmap.CompressFormat.JPEG, dynamicQuality, outputStream) @@ -1660,7 +1725,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { droppedFrameCount++ } if (!screenDataQueue.offer(frameData)) { - Log.e(TAG, "❌ 队列清理后仍无法添加新帧,跳过此帧") + Log.w(TAG, "⚠️ 队列清理后仍无法添加新帧,跳过此帧") droppedFrameCount++ return } @@ -1670,7 +1735,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { checkAndCleanMemory() } catch (e: Exception) { - Log.e(TAG, "❌ 发送帧数据失败", e) + Log.w(TAG, "⚠️ 发送帧数据失败", e) } } @@ -1681,22 +1746,30 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { if (queueProcessingStarted.compareAndSet(false, true)) { Log.d(TAG, "🚀 启动屏幕数据队列处理协程") queueProcessorJob = serviceScope.launch { - while (isCapturing && isActive) { - try { - val frameData = screenDataQueue.poll() - if (frameData != null) { - processFrameData(frameData) - } else { - // 队列为空,短暂休眠避免CPU占用过高 - delay(10) + try { + while (isCapturing && isActive) { + try { + val frameData = screenDataQueue.poll() + if (frameData != null) { + processFrameData(frameData) + } else { + // 队列为空,短暂休眠避免CPU占用过高 + delay(10) + } + } catch (e: CancellationException) { + // 协程正常取消,直接退出循环 + throw e + } catch (e: Exception) { + Log.w(TAG, "⚠️ 队列处理帧异常: ${e.message}") + delay(100) } - } catch (e: Exception) { - Log.e(TAG, "❌ 队列处理协程异常", e) - delay(100) // 出错时延长休眠时间 } + } catch (e: CancellationException) { + Log.d(TAG, "🛑 队列处理协程被取消(正常停止)") + } finally { + Log.d(TAG, "🛑 屏幕数据队列处理协程结束") + queueProcessingStarted.set(false) } - Log.d(TAG, "🛑 屏幕数据队列处理协程结束") - queueProcessingStarted.set(false) } } } @@ -1718,7 +1791,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { Log.d(TAG, "🗑️ 清空队列中剩余的${remainingFrames}帧数据") } } catch (e: Exception) { - Log.e(TAG, "❌ 停止队列处理协程失败", e) + Log.w(TAG, "⚠️ 停止队列处理协程失败", e) } } @@ -1748,7 +1821,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { frameData.fill(0) // 清空数组内容 } catch (e: Exception) { - Log.e(TAG, "❌ 处理帧数据失败", e) + Log.w(TAG, "⚠️ 处理帧数据失败", e) } } @@ -1792,7 +1865,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { } } catch (e: Exception) { - Log.e(TAG, "❌ 内存检查失败", e) + Log.w(TAG, "⚠️ 内存检查失败", e) } } @@ -1824,7 +1897,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { Log.w(TAG, "✅ 紧急内存清理完成") } catch (e: Exception) { - Log.e(TAG, "❌ 紧急内存清理失败", e) + Log.w(TAG, "⚠️ 紧急内存清理失败", e) } } @@ -1872,7 +1945,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { } } catch (e: Exception) { - Log.e(TAG, "❌ 清理WeakReference失败", e) + Log.w(TAG, "⚠️ 清理WeakReference失败", e) } }