diff --git a/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt b/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt index 01765b1..5d28c14 100644 --- a/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt +++ b/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt @@ -435,8 +435,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { } else { // MediaProjection 不可用,回退到无障碍截图 Log.i(TAG, "Android 11+:MediaProjection 不可用,回退到无障碍截图模式") - enableAccessibilityScreenshotMode() - startAccessibilityScreenCapture() + fallbackToAccessibilityCapture() } } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Log.i(TAG, "Android 5-10:使用 MediaProjection VirtualDisplay 连续流式捕获") @@ -464,13 +463,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { // 先确保 MediaProjection 可用 if (!ensureMediaProjection()) { Log.e(TAG, "MediaProjection 不可用,回退到无障碍截图") - // 回退到无障碍截图作为兜底 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - enableAccessibilityScreenshotMode() - startAccessibilityScreenCapture() - } else { - startFallbackCapture() - } + fallbackToAccessibilityCapture() return@launch } @@ -481,12 +474,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { if (virtualDisplay == null) { Log.e(TAG, "VirtualDisplay 创建失败,回退到无障碍截图") - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - enableAccessibilityScreenshotMode() - startAccessibilityScreenCapture() - } else { - startFallbackCapture() - } + fallbackToAccessibilityCapture() return@launch } @@ -505,18 +493,22 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { while (isCapturing) { try { + // MediaProjection对象有效性检测: + // onStop回调会将Holder中的对象清理为null, + // 此时继续采集毫无意义,应立即回退到无障碍截图 + if (mediaProjection == null) { + Log.w(TAG, "MediaProjection对象已失效(被系统回收或onStop清理),立即回退到无障碍截图") + cleanupVirtualDisplayOnly() + fallbackToAccessibilityCapture() + return@launch + } + // Surface有效性检测:BufferQueue被系统abandoned后立即回退 - // 防止acquireLatestImage()持续返回null触发无限重建循环 val currentSurface = imageReader?.surface if (currentSurface == null || !currentSurface.isValid) { - Log.w(TAG, "ImageReader Surface已失效(null=${currentSurface == null}, isValid=${currentSurface?.isValid}), 停止采集并回退到无障碍截图") + Log.w(TAG, "ImageReader Surface已失效(null=${currentSurface == null}, isValid=${currentSurface?.isValid}), 回退到无障碍截图") cleanupVirtualDisplayOnly() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - enableAccessibilityScreenshotMode() - startAccessibilityScreenCapture() - } else { - startFallbackCapture() - } + fallbackToAccessibilityCapture() return@launch } @@ -559,12 +551,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { Log.w(TAG, "MediaProjection连续${consecutiveBlackFrames}个黑屏帧,切换到无障碍截图模式") safeRecycleBitmap(bitmap) cleanupVirtualDisplayOnly() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - enableAccessibilityScreenshotMode() - startAccessibilityScreenCapture() - } else { - startFallbackCapture() - } + fallbackToAccessibilityCapture() return@launch } @@ -600,12 +587,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { if (virtualDisplayRebuildCount > MAX_VIRTUAL_DISPLAY_REBUILD_ATTEMPTS) { Log.w(TAG, "重建次数已达上限,回退到无障碍截图") cleanupVirtualDisplayOnly() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - enableAccessibilityScreenshotMode() - startAccessibilityScreenCapture() - } else { - startFallbackCapture() - } + fallbackToAccessibilityCapture() return@launch } @@ -619,10 +601,7 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { if (virtualDisplay == null || rebuiltSurface == null || !rebuiltSurface.isValid) { Log.w(TAG, "VirtualDisplay 重建后无效(vd=${virtualDisplay != null}, surface=${rebuiltSurface != null}, valid=${rebuiltSurface?.isValid}), 回退到无障碍截图") cleanupVirtualDisplayOnly() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - enableAccessibilityScreenshotMode() - startAccessibilityScreenCapture() - } + fallbackToAccessibilityCapture() return@launch } } @@ -642,12 +621,25 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { } } + /** + * 统一回退到无障碍截图的方法 + * Android 11+ 使用无障碍截图,低版本使用测试图像兜底 + */ + private fun fallbackToAccessibilityCapture() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + enableAccessibilityScreenshotMode() + startAccessibilityScreenCapture() + } else { + startFallbackCapture() + } + } + /** * 确保 MediaProjection 可用 * * 核心修复:禁止重复调用 getMediaProjection(resultCode, resultData) * 每次调用都会创建新实例,系统会自动 stop 旧实例,触发 onStop 回调, - * 形成"权限丢失→恢复→再丢失"的死循环,这是权限频繁掉落的根因。 + * 形成"权限丢失->恢复->再丢失"的死循环,这是权限频繁掉落的根因。 * * 只从 MediaProjectionHolder 获取已有对象,不重新创建。 */ @@ -727,11 +719,19 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { } } - // 截图成功时按帧率延迟,失败时短延迟后立即重试 + // 截图成功时按帧率延迟,失败时等待最小截图间隔后重试 if (consecutiveFailures == 0) { delay(1000 / dynamicFps.toLong()) } else { - delay(50) // 失败时50ms后重试,让系统截图间隔限制自己控制节奏 + // 无障碍截图有系统级最小间隔限制(约3秒), + // 失败时等待剩余间隔时间,避免"截图间隔太短"错误 + val elapsed = System.currentTimeMillis() - lastScreenshotTime + val remaining = MIN_CAPTURE_INTERVAL - elapsed + if (remaining > 0) { + delay(remaining) + } else { + delay(100) + } } } catch (e: CancellationException) {