fix: 修复BufferQueue abandoned导致日志洪泛和无效重建循环

- 捕获循环每帧检测Surface.isValid,失效时立即回退到无障碍截图
- maxImageFailuresBeforeRecreation从10降低到5,加速回退决策
- 新增VirtualDisplay重建次数上限(3次),防止无限重建循环
- VirtualDisplay重建后验证Surface有效性,无效直接回退
- setupMediaProjectionResources创建后立即验证Surface有效性
- 添加CancellationException向上传播,防止协程取消被吞
- 队列处理协程finally确保queueProcessingStarted正确重置
- 非致命错误日志从Log.e降级为Log.w/Log.d减少噪音
This commit is contained in:
wdvipa
2026-02-14 23:03:44 +08:00
parent c7a61b7ae7
commit 548c9a1f15

View File

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